Possible Duplicate:
Java: Rationale of the Object class not being declared abstract
为什么对象类是Java中所有的基类,而不是抽象的?
我有这个问题很长时间了,在这里问这个问题纯粹是出于好奇,仅此而已。我的代码或任何人的代码中没有任何东西是破坏的,因为它不是抽象的,但我想知道为什么它们使它具体化?
为什么会有人想要这个对象类的"实例"(而不是它的存在a.k.a.引用)?一种情况是使用对象实例进行锁定的不好的同步代码(至少我用过一次)。我的坏消息)。
对象类的"实例"有什么实际用途吗?它的实例化如何适合OOP?如果他们将其标记为抽象的(当然,在为其方法提供实现之后),会发生什么?
- +1,它立刻让我想到了同样的问题,但是关于基于.NET的语言
- 关于这个问题似乎有很多困惑;抽象类不需要有抽象方法。相反,问题是"我们有什么真正的理由需要做new Object();"?否则,Object应该是抽象的。
- 另一方面,我想问的更好的问题是:"有没有什么真正的需要,Object应该是抽象的?"没有,所以应该是具体的。
- 提出这个问题的目的正是为了找到使它具体化的原因,因为您需要一个使类抽象化的原因,所以您要使它具体化,对吗?至于需要抽象对象的问题,一个答案是:我们对对象类感兴趣的是它添加到子类中的行为,以及它在继承中特殊的最超级的基类角色。我找不到该类实例的任何用途。如果字符串是最终的,那么必须有一个很好的理由来解释为什么对象是非抽象的,而不仅仅是它们可以使其非抽象的正确性?正确的?
- +这是一个很好的问题。唯一正确的答案可以来自设计师,我们只能提供我们的意见。如果我们只是就为什么它是抽象的给出自己的观点,我想我们永远不会同意,但这很有趣!
- 这是Java世界中最基本的一些东西,所以它设计得很早,经验很少,而不破坏现有代码的Java交易意味着不管它是不是一个好主意,最初的设计决策都会留下来。
如果没有java.lang.Object的设计者告诉我们,我们必须根据自己的意见来回答。有几个问题可以问,这可能有助于澄清。好的。
对象的任何方法都能从抽象中获益吗?好的。
可以说有些方法会从中受益。以hashCode()和equals()为例,如果把这两个概念都抽象化,那么对这两个概念的复杂性就不会那么沮丧了。这将要求开发人员了解它们应该如何实现它们,使它们更加一致(更有效的Java)。但是,我更倾向于认为hashCode()、equals()和clone()属于独立的选择性抽象(即接口)。其他方法,如wait()、notify()、finalize()等,都非常复杂和/或是本地的,所以最好已经实现了,而且不会从抽象中受益。好的。
所以我想答案是否定的,对象的任何方法都不会从抽象中受益。好的。
将对象类标记为抽象类是否有好处?好的。
假设所有方法都实现了,标记对象抽象的唯一效果就是它不能被构造(即new Object()是一个编译错误)。这会有好处吗?我认为"对象"这个词本身是抽象的(你能在你周围找到任何可以完全描述为"对象"的东西吗?)因此,它将适合面向对象的范式。然而,这是纯粹主义的一面。可以说,强制开发人员为任何具体的子类(甚至是空的子类)选择一个名称将导致代码更好地表达他们的意图。我认为,从范式的角度来说,完全正确的做法是,对象应该被标记为abstract,但归根结底,没有真正的好处,这是一个设计偏好的问题(实用主义与纯粹性)。好的。
使用一个简单的对象进行同步的实践是否足够具体化?好的。
许多其他的答案都是关于构建一个简单的对象来用于synchronized()操作。虽然这可能是一种常见且被接受的实践,但我不认为这是一个足够好的理由,如果设计者希望对象是抽象的,那么就可以避免对象是抽象的。其他答案也提到了在我们想要在某个对象上同步的任何时候,我们都必须声明一个单独的空对象子类,但这是站不住的——一个空的子类可以在sdk中提供(java.lang.Lock或其他),可以在我们想要同步的任何时候构造。这样做还有一个额外的好处,那就是创建一个更强有力的意图声明。好的。
是否有任何其他因素可能会受到使对象抽象化的不利影响?好的。
有几个领域,从纯粹的设计角度出发,可能影响了选择。不幸的是,我对它们的了解还不够,无法扩展它们。但是,如果其中任何一项对决定产生影响,我都不会感到惊讶:好的。
还有其他原因吗?好的。
有人提到它可能与反射有关。然而,反射是在物体设计完成后引入的。所以它是否影响反射是没有意义的——这不是原因。仿制药也一样。好的。
还有一个令人难忘的点,java.lang.object是由人类设计的:他们可能犯了错误,他们可能没有考虑过这个问题。没有没有没有没有瑕疵的语言,这可能是其中之一,但如果是的话,那就不是一个大问题。我想我可以放心地说,没有雄心壮志,我不太可能参与设计这种广泛使用的技术的关键部分,尤其是持续15年的技术。多年来一直很强大,所以这不应该被视为批评。好的。
说了这句话,我就把它抽象化了;-p好的。
总结基本上,就我所见,这两个问题的答案是"为什么java.lang.object是具体的?"或者(如果是这样)"为什么java.lang.object是抽象的?"是……"为什么不呢?".好的。好啊。
- 方法本身不必是抽象的——使类成为抽象的要点是不能在代码中调用new Object()。问题是,是否每个人都需要实例化一个Object对象?虽然我同意你的观点,但是hashCode和clone的事情并不好,这与问题没有真正的联系。
- 提到hashcode/clone是为了帮助了解哪些方法可以从抽象中受益,而IMO的答案是"无",所以我认为它是相关的。很好的一点是不能更新一个对象,添加了一些关于这个的信息。
- +如果没有其他的有趣的事情的话。
- 默认的哈希代码非常有用。
- @乔舒亚:我不确定你是不是在讽刺,但我会像你不是那样回答。如果你是,那么这就是我解释你的笑话的地方。—P默认的哈希代码只在你不重写equals的情况下有效,它唯一的应用程序是与基于哈希的集合(例如hashtable)进行正确的交互。对我来说,这是一个很差的关注分离,特别是对于所有基类中最基础的。回到那时,它可能为人们节省了很多时间,但是今天,如果您手工编写hashcode方法,您就错了(tm)。
- @我是认真的。通过显式调用它,我可以得到一个整数形式的对象引用的副本。当尝试调试谁咀嚼了你的单例时,这很有用(对我来说,原来是那个以相同的PID重新启动JVM的人,所以我的另一个工具没有注意到)。
- @约书亚:有趣的用例!但是,据我所知,没有要求该方法必须属于对象。如果它可以在对象中完成,那么它肯定可以在其他地方,即在System和public static int memoryLocationOf(Object toLocate)中完成。事实上,以这种方式拥有它将是一个好处——你不会因为你想要重写equals而失去获取内存位置的能力。除非已经有了一种不使用哈希代码的方法?
- 我只是想回到这里说,我在评论中提到的假设的memoryLocationOf()方法是存在的,就像System.identityHashCode(Object x)一样。
- 您可以使用new Object() {}生成一个与其他任何类不同的对象。
- @Grundlefleck:至少可以定义两个等价关系,允许一个对任何类对象进行有意义的比较(如果一个苹果实例被问到它等于一个橙色的实例,它不应该抱怨;它应该简单地说它不等于)。equals和hashCode的语义看起来模糊的原因是,对象被强制重写一组等价方法来定义它们认为调用者想要的关系,而不是让调用者指定这两个关系中哪一个是感兴趣的(注意,有可能……
- …某个对象实例的消费者有时对等价关系感兴趣,而另一个对象实例的消费者则对等价关系感兴趣)。仅供参考,一种关系认为,如果不希望将任何对Y的引用与对X的引用的任意组合替换为对Y的引用来改变语义,则X和Y等同。另一方认为,如果将对X的所有引用与对Y的引用进行交换(反之亦然)不会影响语义的更改,则它们是等效的。第一个有效测试永久等效;第二个有效测试状态等效。
- @格鲁德弗莱克你关于equals和hashCode的声明说,这将要求开发人员弄清楚他们应该如何实现它们,使它们更明显地一致,这不足以成为equals和hashCodeabstract的充分理由。典型的应用程序包含不同种类的类。包含业务逻辑的类、将控制从应用程序的一部分发送到另一部分的类等。这些类不会添加到哈希Collection中,也不需要检查它们是否相等。equals和hashCode是可选的。
- @我不太明白你的意思。我的观点是,这些方法应该在可选接口上,这正是您提到的原因——并不是每个类都被哈希/检查是否相等。你能详细解释一下,帮我理解一下吗?
- @格伦德弗莱克我的坏。我可能在没有阅读你关于选择抽象的观点的情况下得出了一个结论。但是,将equals作为对象的一部分允许程序员始终使用equals来编程到接口,即使他们正在检查两个引用是否指向同一个对象。如果他们后来改变了对他们来说平等意味着什么的想法,他们可以在不更改客户机代码的情况下重写equals。
java.lang.Object的普通实例通常用于锁定/同步场景,这是公认的做法。
还有——为什么它是抽象的?因为它作为一个实例本身并不能完全发挥作用?它真的能和一些抽象的成员有关吗?别这么想。因此,首先把它抽象化的论点是不存在的。事实并非如此。
以动物的经典层次结构为例,其中有一个抽象类Animal,使Animal类抽象的理由是,动物的一个实例实际上是"无效的"——因为缺少一个更好的单词动物(即使它的所有方法都提供了基本实现)。对于Object,情况并非如此。首先,没有压倒性的理由将其抽象化。
- 听上去像个黑客
- @BlueRaja请解释为什么在一个好的旧私有最终对象上同步可能是一个黑客?
- 这里是吹毛求疵,但我相信如果使用字节[0]而不是对象作为锁,开销会稍微低一些。
- @阿达姆斯基:这种信仰是基于什么?
- 如果这是对象不抽象的唯一原因,那么它肯定是一个黑客;为什么不在API中创建一个互斥对象呢?
- 因为任何对象都可以充当Java中的互斥体,所以没有理由为它提供专用类。
- @但是你还没有解释为什么会是"黑客"。
- @迈克尔:创建一个对象比使用字节[0]生成的字节码稍微多一些。显然,两者之间的差别是微不足道的。
- @WIM:我的意思是,对于设计人员来说,这将是一次黑客攻击——如果他们将对象变为非抽象对象的意图确实是为了将它们用作同步对象,那么设计人员就应该为此专门制作一个同步对象。我仍然不明白为什么对象应该是非抽象的。
- 不管怎样,除非语言设计者说"我们使Object具体化以便它可以用于同步",否则这不是一个正确的答案。仅仅因为人们最终把它用于同步并不意味着这就是为什么它被具体化的原因。我就是这么看的。
- 我没有说这就是为什么物体是具体的。只是将其用于所述场景是非常常见且被广泛接受的实践。
- @字节[0]是一个对象,它支持java.lang.object的所有方法,并且有一个需要初始化的长度字段。我不明白它怎么可能减少开销。
- @我知道,但这不是被问到的问题。
- 在那里,我们现在有一个(1)目的地。
- Wim,我理解这一点,但问题不是"使用普通的Java.Lang.Object的常见和接受的用法是什么?"如果是的话,你会得到我的+1:-)。但答案是,我不认为它是一个+1(但不确定它是否值得-1)。
- 或者使用现在必须被视为陈词滥调的相关性并不意味着因果关系。-P
- @michael-尝试生成两个类:一个类在main()方法中创建一个object(),另一个类创建byte[0],然后在字节代码编辑器中打开字节代码:在调用初始值设定项时,您将看到object()还有额外的开销。
- +1没有设计师的评论,"抽象没有好处"对我来说似乎更合理。尽管我可能不同意没有好处…
- …我对你的Animal类比表示怀疑。你能找到或想到任何完全用"物体"这个词描述的东西吗?我认为动物和物体在这方面是相同的——它们都是抽象的术语。这并不是说它是抽象的,而是有一个范例概念和语用语言力学的论点。
我可以考虑几个案例,其中Object的实例很有用:
- 锁定和同步,就像你和其他评论提到的。这可能是一种代码味道,但我一直看到对象实例以这种方式使用。
- 作为空对象,因为除实例本身之外,equals始终返回false。
- 在测试代码中,尤其是在测试集合类时。有时,用虚拟对象而不是null来填充集合或数组是最容易的。
- 作为匿名类的基实例。例如:
Object o = new Object() {...code here...}
- "作为匿名类的基实例。"->当对象是抽象的时,也可以这样做。
- 所有非常有效的点,但当对象是抽象的时,所有的都可以用其他方法来完成。
从我读到的每一篇文章来看,Object似乎不需要具体化,实际上应该是抽象的。
不仅不需要具体化,而且在阅读了更多的内容之后,我相信不是抽象的Object与基本继承模型冲突——我们不应该允许具体类的抽象子类,因为子类只应该添加功能。显然,Java中不是这样的,我们有EDCOX1的抽象子类0。
- @BlueRaja对我的回答不完全正确,但你的回答如何回答这个问题:"为什么对象不是抽象的?"和它一起,这里的大多数其他答案也在这里。
- @WIM:除了"这是一个设计错误",似乎没有一个真正的答案,因此"对象应该是抽象的"。
- @维姆:因为我也回答了你的回答,所以我假设你也认为我在"欺骗你"。请注意,我对攻击个人没有兴趣,我只是想在网站上看到最优质的答案,我通过询问你的答案来做到这一点。没有恶意,也没有"蓄意"的意思。我显然不能代表布拉拉贾说话,但在我看来,他也是这样做的。
- we should not be allowing abstract subclasses of a concrete class.为什么?
- 我不明白用抽象类扩展一个具体的类是如何违反添加功能的思想的。我可以有一个具体的类A,它提供4个函数,然后用类B扩展它,它还提供1个函数,但是这个函数是抽象的,因为对于不同的子类可以有不同的实现。例如,员工是具体的,具有记录工时和注册时间计划等功能。然后我用计算佣金的销售员来扩展它,但这是抽象的,因为我们有不同佣金规则的销售员子类。
- @不,没关系,我明白。我读得太多了,我相信布鲁拉贾也不是那个意思。我不应该用这种情绪化的术语。;-)
- @杰伊:用is-a的术语,如果a是具体事物,b是a,那么它应该遵循b是具体事物的理由。如果您使用未实现的添加方法将a扩展到b,那么99/100倍于您真正想要的是一个接口(另一个1/100,您真正想要的是将这些方法推到a并形成一个抽象)。
- 我个人并不认为"不需要具体化"=="事实上应该是抽象的。"而且,您提供的链接只简单地说"应该是",然后继续讨论当决定它是抽象的时,可以做什么其他的事情。你提供的参考似乎并不能为你的论点提供任何支持。所以我同意"它不需要具体化",但我还是想知道为什么它应该是抽象的。我还没有看到有人意外地创造了一个物体而产生任何缺陷。它将如何受益?
- @安德鲁:实例化一个对象类型的一个可能的缺点是该类型根本不传达意图。被授予的变量/方法名可以完成这项工作,但我认为如果我看一下,我可以找到一个合适的、真实的例子。我仍然看到"不需要具体化"=="应该是抽象的",我想应该是味道了:—)
- @布鲁拉贾:好的,我们用"是"的术语。销售员是雇员。全职销售员是个销售员。兼职销售员是个销售员。销售员是抽象的,因为它有只能在全职/兼职级别实现的方法。这是如何违反"是一个"概念的?我正在每个级别添加功能。事实上,在一个层面上,我们创造了一个二元性,直到下一个层面对我来说是完全合法的,才得以解决。(继续…)
- 现在,如果有人说员工有一个具体的功能,而那个销售人员将这个功能抽象化,那么您将删除信息。(我认为这不可能在爪哇做,但不管怎样)但是如果推销员增加了一个抽象函数,直到我们到达PultimeSaleSman和PoTimeSaleSman,没有任何信息在任何步骤被减去。
- @杰伊:如果我设计的话,我会让员工抽象化,让销售人员具体化;全职/兼职状态是员工的财产。如果有足够多的其他类型的员工收取佣金,我可以创建一个抽象的CommissionEmployee类,或者创建一个ICollectsCommission接口,这取决于(dis)计算佣金的算法的相似性。
- @布鲁拉贾:我在构建这个例子时的假设是:(a)有员工,我们不是销售人员(一个不合理的假设),我们不需要进一步区分谁,所以员工必须是具体的。(b)全职/兼职区别仅在计算佣金时与我们的系统相关,因此仅适用于销售人员。如果你坚持认为所有的员工都可以被归类为FT或PT,好吧,好吧,如果你愿意的话,把它定为"零售销售员"和"批发销售员"。(继续…)
- 在任何情况下,如果问题是,"我能想出一个不违反你的‘抽象不能扩展具体’规则的解决方案吗,即使它比其他可能的解决方案更复杂和不自然?"我确定答案是"是"。但你为什么要这么做?这又回到了我原来的问题:这样的规则有什么作用?为什么这是一个好的规则,我们甚至想考虑遵循?
- @杰伊:我想这样做是因为设计更干净;这就是为什么存在abstract关键字(比如final)。
- 它并没有使设计变得更清晰,当然也不是抽象关键字存在的原因。
我认为它应该已经被宣布为抽象的,但是一旦它被完成并发布,很难撤销而不会造成很多的痛苦。
"如果将非抽象类更改为声明为抽象类,则尝试创建该类新实例的现有二进制文件将在链接时抛出InstantiationError,或者(如果使用反射方法)在运行时抛出InstantiationException;因此,不建议对广泛分布的类进行这种更改。"."
- 有趣。这个附加内容是关于将对象抽象化的讨论,还是一般意义上的讨论?
- IIRC这只是一章的一部分,提供了通用的二进制兼容性建议。
我不明白为什么大多数人似乎都认为制作一个完全功能类是一个好主意,它以一种使用完全抽象的方式实现它的所有方法。
我宁愿问为什么要把它抽象化?它有什么不该做的吗?它是否缺少了一些应该具备的功能?这两个问题都可以用"否"来回答,它本身就是一个完全工作的类,使其抽象化只会导致人们实现空类。
1
| public class UseableObject extends AbstractObject{} |
useableobject继承自抽象对象,可以实现它,它不添加任何功能,它存在的唯一原因是允许访问对象公开的方法。
我也不同意在"差"同步中的使用。使用私有对象来同步访问比使用同步(这个)更安全,而且比Java UITL并发的锁类更容易使用。
- 回答"为什么要抽象化?"我会说,因为它并不意味着什么是"一个物体"。实际上,"对象"这个词是一个抽象的词。我的意思是,如果你环顾四周,你将找不到任何你能完全描述为"一个物体"的东西,对于一个具体的事物,总会有一个具体的术语。我想这是一种实用主义的折衷,即允许一些有用但不真正有意义的东西与精确的范式概念相比较。这没什么大不了的,但想起来很有趣:)
- "Grundlefleck,他们可以称之为MimulalJavaObjor,然后描述Java中任何对象的最小实现,这是具体的,但我必须承认,确实存在一个实用主义的折衷方案,我有Jet去看一个完美无瑕的编程语言:"
- 嗯,我想这是有道理的。
有时您需要一个没有自己状态的普通对象。虽然这些物体乍一看似乎没用,但它们仍然有效用,因为每一个都有不同的特性。TNIS在一些场景中很有用,其中最重要的是锁定:您希望协调两个线程。在爪哇中,使用一个被用作锁的对象来实现这一点。对象不需要任何状态,它的存在足以使它成为一个锁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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()。
如果对象是抽象的,我们需要一个锁,那么我们就必须在不添加任何方法或字段的情况下对它进行子类化,这样我们就可以实例化锁。
想一想,这里有一个双重问题:假设对象是抽象的,它会定义任何抽象方法吗?我想答案是否定的。在这种情况下,将类定义为抽象类没有多大价值。
- 隐马尔可夫模型。。。最近使用新java.util.concurrent的东西会不会更好呢?
- 你知道有什么证据表明物体不是抽象的吗?当然,它是有用的,但它是否使最初的决定具体化了呢?
- "如果对象是抽象的,我们需要一个锁,那么我们就必须在不添加任何方法或字段的情况下对其进行子类化,这样我们就可以实例化锁。"—是的,但是在SDK中,这可以完成一次,因此您可以使用java.lang.lock(或其他)对象,并且最好表达您的意图。并不是每个人每次想要同步一个锁的时候都必须创建一个新的空类。
- @Grundlefleck:使Object具体化的一个缺点是它迫使每个对象向外部世界公开其身份;从OOP的角度来看,如果代码说ImmutablePoint foo1 = ImmutablePoint.Create(3,4), foo2 = ImmutablePoint.Create(3,4);,外部代码就没有特别的理由能够分辨foo1和foo2是否是ImmutablePoint的不同实例。如果像synchronized这样的东西只与实现IdentifiableByReference的类型一起工作,那么可以支持不可变对象封装相同数据的抽象不可区分。
在我看来,这里有一个简单的实用性问题。将类抽象化会剥夺程序员做某事的能力,即实例化它。对于一个抽象类,你不能做任何你不能做的事情。(好吧,您可以在其中声明抽象函数,但在本例中,我们不需要有抽象函数。)因此,通过使其具体化,您可以使其更灵活。
当然,如果通过使其具体化而造成一些积极的伤害,那么"灵活性"将是一个缺点。但我想不出任何使对象可实例化的主动伤害。(是否"可实例化"一词?不管怎样),我们可以讨论某人对原始对象实例的任何给定使用是否是一个好主意。但即使你能说服我,我所见过的每一个原始对象实例的使用都是一个坏主意,那仍然不能证明那里可能没有好的用途。所以,如果它不伤害任何东西,而且它可能会有帮助,即使我们现在不能想到它实际上会有帮助的方法,为什么要禁止它呢?
我认为迄今为止所有的答案都忘记了Java 1是什么样的。在Java 1中,你不能制作一个匿名类,所以如果你只是为了某个目的而需要一个对象(同步或空占位符),那么你就必须为此声明一个类,然后一堆代码就有了这些额外的类。更直接地向前,只允许对象的直接实例化。
当然,如果你现在在设计Java,你可能会说每个人都应该这样做:
但这不是1.0中的一个选项。
我怀疑设计者不知道未来人们使用对象的方式,因此不想通过强制程序员在不必要的地方创建一个额外的类来限制他们,例如对于互斥锁、键等。
它还意味着它可以在数组中实例化。在1.5天前,这将允许您拥有通用的数据结构。在某些平台上仍然是这样(我在考虑J2ME,但我不确定)
- 例如,您可以有一个类型为Object[]的泛型数组,即使Object是抽象的——您只需要用Object子类型填充它。
对象需要具体化的原因。
反射请参见object.getClass()。
通用用途(前Java 5)
比较/输出请参见object.toString()、object.equals()、object.hashcode()等。
同步化请参见object.wait()、object.notify()等。
尽管已经替换了两个区域,但仍然需要一个具体的父类来向每个Java类提供这些特性。
对象类用于反射,因此代码可以对不确定类型的实例调用方法,即"object.class.getDeclaredMethods()"。如果对象是抽象的,那么想要参与的代码必须实现所有抽象方法,然后客户机代码才能对其使用反射。
根据Sun的说法,抽象类是一个声明为抽象的类,它可以包含也可以不包含抽象方法。抽象类不能被实例化,但可以被子类化。这也意味着您不能调用方法或访问抽象类的公共字段。
抽象根类示例:
1 2 3 4 5 6 7 8 9 10
| abstract public class AbstractBaseClass
{
public Class clazz;
public AbstractBaseClass(Class clazz)
{
super();
this.clazz = clazz;
}
} |
AbstractBaseClass的一个子类:
1 2 3 4 5 6 7 8 9 10 11 12
| public class ReflectedClass extends AbstractBaseClass
{
public ReflectedClass ()
{
super(this);
}
public static void main (String[] args )
{
ReflectedClass me = new ReflectedClass ();
}
} |
这不会编译,因为在构造函数中引用"this"是无效的,除非它调用同一类中的另一个构造函数。如果我将其更改为:
1 2 3 4
| public ReflectedClass()
{
super(ReflectedClass.class);
} |
但这只起作用,因为ReflectedClass有一个父级("Object"),它是1)Concrete,2)有一个字段来存储其子级的类型。
更典型的反射示例是在非静态成员函数中:
1 2 3 4
| public void foo()
{
Class localClass = AbstractBaseClass.clazz;
} |
除非将字段"clazz"更改为静态,否则将失败。对于对象的类字段,这不起作用,因为它应该是特定于实例的。对象具有静态类字段是没有意义的。
现在,我确实尝试了下面的更改,它是有效的,但有点误导。它仍然需要扩展基类才能工作。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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之前的泛型(比如数组)是不可能的。
1 2 3
| Object[] array = new Object[100];
array [0] = me ;
array [1] = meTwo ; |
在接收到实际对象之前,需要构造实例作为占位符。
- 他们不需要在编译之前实现所有的抽象方法吗?
- 再一次,仅仅因为类本身是抽象的,并不意味着方法必须是抽象的。查看对Girinie答案的评论。
- @blueraja,重点不是要实现什么,而是只要有未实现的抽象方法,就不能调用object.class。选择是对象具体化,或者每个想要参与反射的类都必须为任何抽象方法提供实现。换句话说,反射是物体不抽象的原因之一。
- @凯莉,我还是不明白你的意思。"参与反射"与"可以编译"有何不同?我对反射不太熟悉,所以这可能是我无法连接点的原因。子类如何影响对Object.class.getDeclaredMethods()的调用?当然,它们只是返回设置了抽象标志的方法?
- @凯利:"必须为任何抽象方法提供实现。"—不会有任何抽象方法。
- @一个没有抽象方法的抽象类?问题是问为什么对象不是抽象的。对于实现抽象类的语言规则的关注似乎忽略了问题的要点。
- @凯莉:……是的,没错。因为没有抽象方法,所以说"客户机在使用反射之前必须实现所有的抽象方法"是一个没有意义的观点;反射的工作原理是一样的。
- @Grundlefleck:如果在类可以用于反射之前必须从对象实现任何未实现的抽象方法,那么您就不必费心了。为了使反射具有通用性,需要有一个可以实例化的父类,以便可以对其调用方法。这排除了java.lang.object是抽象基类的可能性。
- 您可能想从.class开始检查@since。类对象(和一般的反射)是在1.1版本中添加的,这是在设计决策使对象具体化之后。
- @凯莉:我不明白这一点,是不是这样你就不需要知道确切的类型了?忘记了这一点,自java.lang.object被编写以来反射是否可用?如果不是这样的话,那么这一切都是没有意义的:)
- @凯利:你的代码中有一些部分由于不同于你想象的原因而不起作用。ReflectedClass不能调用super(this),因为this不是Class类型。试试super(this.getClass())。另外,您不能引用AbstractBaseClass.clazz,因为该字段没有声明为static,您试图通过类而不是实例引用它。如果您有一个实例,您将能够引用字段,即instanceOfSubclass.clazz。还是我错过了什么?
- @格伦德弗莱克:谢谢你的纠正。我把abstractBaseClass.clazz移到了一个非静态方法中,但在我的编辑中没有遇到这种情况。我会解决的。至于super(这个),我试图了解的更多的事实是,对象有可以调用的方法,而无需子类化它,这仅仅是因为它是具体的。最后的数组示例可能是更好的例子。关于通过实例引用字段的注释是正确的,它是有效的,因为我创建了一个实例,但在某些地方使用object.somemethod()时没有实例。
- 凯莉:"……"有些地方使用object.somemethod()时没有实例"I'm still lost",因为在abstract class上声明static方法(而不是abstract方法)是有效的,并且不会产生任何问题。对于数组示例,为什么您需要有占位符,为什么不在接收对象之前将位置留空呢?
- @亚历克斯:谢谢你的推荐信。我不再自欺欺人了,把这个答案编辑成:"这不是为什么物体是抽象的。此消息将在3、2、1、++AT0中自毁+++
我怀疑简短的答案是,集合类在Java泛型之前的几天内丢失了类型信息。如果集合不是泛型的,那么它必须返回一个具体的对象(并且在运行时向下强制转换为以前的任何类型)。
因为将一个具体的类变为抽象类会破坏二进制兼容性(如上面提到的那样),所以保留了具体的对象类。我想指出的是,在任何情况下,它都不是为实现同步而创建的;虚拟类也可以工作。
设计缺陷从一开始就不包括泛型。很多设计批评都是针对这个决定及其后果的。[哦,还有数组子类型规则。]
- "如果集合不是通用的,那么它必须返回一个具体的对象"-我不理解这个注释。集合可以保存/返回抽象类型,它只是不能-也不需要-实例化它们(因此抽象类型的所有"实例"实际上都是子类型的实例)。这就是它一直工作的方式。
- -1、集合类不需要返回具体的对象,它们会将其他对象(如字符串)强制转换为对象。
- 即使使用泛型,并且由于类型擦除,集合也不会丢失类型信息吗????
- +1 Andrew的注释-泛型类型是针对编译器的,没有关于类型的运行时信息。据我所知,向下转换仍然会发生(编译器插入转换并确保它们是安全的)。但是,集合可以返回一个抽象类型,我认为抽象与具体对集合没有影响。实际上,当对象被写入时,集合类是否存在?二进制兼容性是它现在不能改变的原因,而不是它被设计成这样开始的原因。
它不是抽象的,因为每当我们创建一个新的类时,它会扩展对象类,如果它是抽象的,那么您需要实现对象类的所有方法,这是开销…该类中已经实现了方法…
- 您只需要重新实现抽象方法,而不是每个方法。
- 隐马尔可夫模型。。。。这个问题引出了对抽象类是什么的一些非常基本的误解……
- 只有抽象方法需要重写/实现,而不是抽象类的所有方法。
- 我的意思是说抽象的方法……我需要实现
- 是的,但是你可以使对象抽象,而不必使它的任何方法抽象。