代码之家  ›  专栏  ›  技术社区  ›  Elister

为什么java.lang.object不是抽象的?[复制品]

  •  125
  • Elister  · 技术社区  · 14 年前

    可能重复:
    Java: Rationale of the Object class not being declared abstract

    为什么对象类是Java中所有的基类,而不是抽象的?

    我有这个问题很长时间了,在这里问这个问题纯粹是出于好奇,仅此而已。我的代码或任何人的代码中没有任何东西是破坏的,因为它不是抽象的,但我想知道为什么它们使它具体化?

    为什么会有人想要这个对象类的“实例”(而不是它的存在a.k.a.引用)?一种情况是使用对象实例进行锁定的不好的同步代码(至少我用过一次)。我的坏消息)。

    对象类的“实例”有什么实际用途吗?它的实例化如何适合OOP?如果他们将其标记为抽象的(当然,在为其方法提供实现之后),会发生什么?

    14 回复  |  直到 14 年前
        1
  •  63
  •   Grundlefleck anujkk    14 年前

    没有设计师 java.lang.Object 告诉我们,我们的答案必须以意见为基础。有几个问题可以问,这可能有助于澄清。

    对象的任何方法都能从抽象中获益吗?

    可以说有些方法会从中受益。采取 hashCode() equals() 例如,如果把这两个问题都抽象化,那么他们对这两个问题的复杂性就不会那么沮丧了。这将要求开发人员了解它们应该如何实现它们,使它们更加一致(更有效的Java)。不过,我更倾向于认为 哈希德() , 等式() clone() 属于单独的抽象(即接口)。其他方法, wait() , notify() , finalize() 等等足够复杂和/或是本地的,所以最好已经实现了它们,并且不会从抽象中受益。

    所以我想答案是否定的,对象的任何方法都不会从抽象中受益。

    将对象类标记为抽象类是否有好处?

    假设所有方法都实现了,标记对象抽象的唯一效果就是它不能被构造(即 new Object() 是编译错误)。这会有好处吗?我认为“对象”这个词本身是抽象的(你能在你周围找到任何可以完全描述为“对象”的东西吗?)因此,它将适合面向对象的范式。但是,在 纯粹主义者 一边。可以说,强制开发人员为任何具体的子类(甚至是空的子类)选择一个名称将导致代码更好地表达他们的意图。我想,完全是 对的 就范式而言,对象应该被标记 abstract 但归根结底,没有真正的好处,这是一个设计偏好的问题(实用主义与纯粹)。

    使用一个简单的对象进行同步的实践是否足够具体化?

    许多其他的答案都是关于构造一个简单的对象来在 synchronized() 操作。虽然这可能是一种常见且被接受的实践,但我不认为这是一个足够好的理由,如果设计者希望对象是抽象的,那么就可以避免对象是抽象的。其他答案也提到了在我们想要在某个对象上同步的任何时候,我们都必须声明一个单独的空对象子类,但这是站不住的——一个空的子类可以在SDK中提供。( java.lang.Lock 或者其他的),可以在我们想要同步的任何时候构建。这样做还有一个额外的好处,那就是创建一个更强有力的意图声明。

    是否有任何其他因素可能会受到使对象抽象化的不利影响?

    有几个领域,从纯粹的设计角度出发,可能影响了选择。不幸的是,我对它们的了解还不够,无法扩展它们。但是,如果其中任何一项对决定产生影响,我都不会感到惊讶:

    • 性能
    • 安全性
    • JVM实现的简单性

    还有其他原因吗?

    有人提到它可能与反射有关。然而,反射是在物体设计完成后引入的。所以,它是否影响反射是没有意义的——这不是原因。仿制药也一样。

    还有一个令人难忘的观点,java.lang.object是由人类设计的:他们可能犯了错误,他们可能没有考虑过这个问题。没有语言没有缺陷,这 可以 成为他们中的一员,但如果是的话,那就不是什么大问题了。我想我可以放心地说,没有雄心壮志,我不太可能参与设计这种广泛使用的技术的关键部分,尤其是持续15年的技术。多年来一直很强大,所以这不应该被视为批评。

    说了这句话,我就把它抽象化了;-p

    总结
    基本上,就我所见,这两个问题的答案是“为什么java.lang.object是具体的?”或者(如果是这样)“为什么java.lang.object是抽象的?”是…”为什么不呢?”.

        2
  •  24
  •   Wim    14 年前

    的普通实例 java.lang.Object 通常用于锁定/同步场景,这是公认的做法。

    还有——为什么它是抽象的?因为它作为一个实例本身并不能完全发挥作用?它真的能和一些抽象的成员有关吗?别这么想。因此,首先把它抽象化的论点是不存在的。事实并非如此。

    以动物的经典层次结构为例,在这里你有一个抽象类 Animal 的理由 动物 类抽象是因为animal的一个实例实际上是一个“无效”的,因为缺少更好的单词animal(即使它的所有方法都提供了一个基本实现)。用 Object 但事实并非如此。首先,没有压倒性的理由将其抽象化。

        3
  •  11
  •   Brett Daniel    14 年前

    我可以想到几个例子 Object 有用:

    • 锁定和同步,就像你和其他评论提到的。这可能是一种代码味道,但我一直看到对象实例以这种方式使用。
    • 作为 Null Objects ,因为 equals 将始终返回false,实例本身除外。
    • 在测试代码中,尤其是在测试集合类时。有时用虚拟对象填充集合或数组,而不是 null S.
    • 作为匿名类的基实例。例如:
      Object o = new Object() {...code here...}
        4
  •  10
  •   BlueRaja - Danny Pflughoeft    14 年前

    从我读到的每一件事来看,似乎 Object 不需要具体化 事实上 应该 是抽象的。

    它不仅不需要具体化,而且经过一些 more reading 我相信 对象 抽象与基本继承模型冲突-我们不应该允许具体类的抽象子类,因为子类只应该 添加 功能。
    显然,Java中不是这样的,我们有抽象的子类。 对象 .

        5
  •  7
  •   ishmeister    14 年前

    我认为它应该已经被宣布为抽象的,但是一旦它被完成并发布,很难撤销而不会造成很多的痛苦。

    “如果将非抽象类更改为声明为抽象类,则尝试创建该类新实例的现有二进制文件将在链接时抛出InstantiationError,或者(如果使用反射方法)在运行时抛出InstantiationException;因此,不建议对广泛分布的类进行这种更改。”."

        6
  •  5
  •   Itay Maman    14 年前

    有时您需要一个没有自己状态的普通对象。虽然这些物体乍一看似乎毫无用处,但它们仍然有效用,因为每一个都有不同的用途。 身份 . TNIS在一些场景中很有用,其中最重要的是锁定:您希望协调两个线程。在爪哇中,使用一个被用作锁的对象来实现这一点。对象不需要任何状态,它的存在足以使它成为一个锁:

    class MyThread extends Thread {
       private Object lock;
       public MyThread(Object l) { lock = l; }
    
       public void run() { 
    
          doSomething();
          synchronized(lock) {
              doSomethingElse();
          } 
       }
    }
    
    Object lock = new Object();
    new MyThread(lock).start();
    new MyThread(lock).start();
    

    在这个例子中,我们使用一个锁来防止两个线程同时执行 doSomethingElse()

    如果对象是抽象的,我们需要一个锁,那么我们就必须在不添加任何方法或字段的情况下对它进行子类化,这样我们就可以实例化锁。

    想一想,这里有一个双重问题:假设对象是抽象的,它会定义任何抽象方法吗?我想答案是 . 在这种情况下,将类定义为抽象类没有多大价值。

        7
  •  5
  •   josefx    14 年前

    我不明白为什么大多数人似乎都相信创建一个完全功能类,它以一种使用完全抽象的方式实现它的所有方法是一个好主意。
    我宁愿问为什么要把它抽象化?它有什么不该做的吗?它是否缺少了一些应该具备的功能?这两个问题都可以用“否”来回答,它本身就是一个完全工作的类,使其抽象化只会导致人们实现空类。

    public class UseableObject extends AbstractObject{}
    

    useableobject继承自抽象对象,并且可以实现它,它不添加任何功能,它存在的唯一原因是允许访问对象公开的方法。
    我也不同意在“差”同步中的使用。使用私有对象来同步访问比使用同步(这个)更安全,而且比Java UITL并发的锁类更容易使用。

        8
  •  2
  •   Jay    14 年前

    在我看来,这里有一个简单的实用性问题。将类抽象化会剥夺程序员做某事的能力,即实例化它。对于一个抽象类,你不能做任何你不能做的事情。(好吧,您可以在其中声明抽象函数,但在本例中,我们不需要有抽象函数。)因此,通过使其具体化,您可以使其更灵活。

    当然,如果通过使其具体化而造成一些积极的伤害,那么“灵活性”将是一个缺点。但我想不出任何使对象可实例化的主动伤害。(是否“可实例化”一词?不管怎样),我们可以讨论某人对原始对象实例的任何给定使用是否是一个好主意。但即使你能说服我,我所见过的每一个原始对象实例的使用都是一个坏主意,那仍然不能证明那里可能没有好的用途。所以,如果它不伤害任何东西,而且它可能会有帮助,即使我们现在不能想到它实际上会有帮助的方法,为什么要禁止它呢?

        9
  •  1
  •   Pool    14 年前

    我怀疑设计者不知道未来人们使用对象的方式,因此不想通过强制程序员在不必要的地方创建一个额外的类来限制他们,例如对于互斥锁、键等。

        10
  •  1
  •   Yishai    14 年前

    我认为迄今为止所有的答案都忘记了Java 1是什么样的。在Java 1中,你不能制作一个匿名类,所以如果你只是为了某个目的而需要一个对象(同步或空占位符),那么你就必须为此声明一个类,然后一堆代码就有了这些额外的类。更直接地向前,只允许对象的直接实例化。

    当然,如果你现在在设计Java,你可能会说每个人都应该这样做:

     Object NULL_OBJECT = new Object(){};
    

    但这不是1.0中的一个选项。

        11
  •  0
  •   malaverdiere    14 年前

    它还意味着它可以在数组中实例化。在1.5天前,这将允许您拥有通用的数据结构。在某些平台上仍然是这样(我在考虑J2ME,但我不确定)

        12
  •  -4
  •   Kelly S. French    14 年前

    对象需要具体化的原因。

    1. 反射
      请参见object.getClass()。

    2. 通用用途(前Java 5)

    3. 比较/输出
      请参见object.toString()、object.equals()、object.hashcode()等。

    4. 同步化
      请参见object.wait()、object.notify()等。

    尽管已经替换了两个区域,但仍然需要一个具体的父类来向每个Java类提供这些特性。

    对象类用于反射,因此代码可以对不确定类型的实例调用方法,即“object.class.getDeclaredMethods()”。如果对象是抽象的,那么想要参与的代码必须实现所有抽象方法,然后客户机代码才能对其使用反射。

    据孙说, An abstract class is a class that is declared abstract—it may or may not include abstract methods. Abstract classes cannot be instantiated, but they can be subclassed. 这也意味着您不能调用方法或访问抽象类的公共字段。

    抽象根类示例:

    abstract public class AbstractBaseClass
    {
        public Class clazz;
    
        public AbstractBaseClass(Class clazz)
       {
           super();
           this.clazz = clazz;
       }
    }
    

    AbstractBaseClass的一个子类:

    public class ReflectedClass extends AbstractBaseClass  
    {
        public ReflectedClass() 
        {
           super(this);
        }
    
        public static void main(String[] args)  
        {
            ReflectedClass me = new ReflectedClass();
        }
    }
    

    这不会编译,因为在构造函数中引用“this”是无效的,除非它调用同一类中的另一个构造函数。如果我将其更改为:

       public ReflectedClass()
       {
           super(ReflectedClass.class);
       }  
    

    但这只起作用,因为ReflectedClass有一个父级(“Object”),它是1)Concrete,2)有一个字段来存储其子级的类型。

    反射的一个更典型的例子是在非静态成员函数中:

    public void foo()
    {
        Class localClass = AbstractBaseClass.clazz;  
    }
    

    除非将字段“clazz”更改为静态,否则将失败。对于对象的类字段,这不起作用,因为它应该是特定于实例的。对象具有静态类字段是没有意义的。

    现在,我确实尝试了下面的更改,它是有效的,但有点误导。它仍然需要扩展基类才能工作。

    public void genericPrint(AbstractBaseClass c)
    {
        Class localClass = c.clazz;
        System.out.println("Class is: " + localClass);
    }
    
    public static void main(String[] args)
    {
        ReflectedClass me = new ReflectedClass();
        ReflectedClass meTwo = new ReflectedClass();
    
        me.genericPrint(meTwo);
    }
    

    Java5之前的泛型(比如数组)是不可能的。

    Object[] array = new Object[100];
    array[0] = me;
    array[1] = meTwo;
    

    在接收到实际对象之前,需要构造实例作为占位符。

        13
  •  -6
  •   BJ.    14 年前

    我怀疑简短的答案是,集合类在Java泛型之前的几天内丢失了类型信息。如果集合不是泛型的,那么它必须返回一个具体的对象(并且在运行时向下强制转换为以前的任何类型)。

    因为将一个具体的类变为抽象类会破坏二进制兼容性(如上面提到的那样),所以保留了具体的对象类。我想指出的是,在任何情况下,它都不是为实现同步而创建的;虚拟类也可以工作。

    设计缺陷从一开始就不包括泛型。很多设计批评都是针对这个决定及其后果的。[哦,还有数组子类型规则。]

        14
  •  -9
  •   giri    14 年前

    它不是抽象的,因为每当我们创建一个新的类时,它会扩展对象类,如果它是抽象的,那么您需要实现对象类的所有方法,这是开销…该类中已经实现了方法…