【发布时间】:2010-12-19 23:58:06
【问题描述】:
假设我有:
class A {
Integer b;
void c() {}
}
为什么Java有这样的语法:A.class,却没有这样的语法:b.field,c.method?
类文字有什么用途如此普遍吗?
【问题讨论】:
假设我有:
class A {
Integer b;
void c() {}
}
为什么Java有这样的语法:A.class,却没有这样的语法:b.field,c.method?
类文字有什么用途如此普遍吗?
【问题讨论】:
A.class 语法看起来 像一个字段访问,但实际上它是一个特殊语法规则的结果,在一个根本不允许正常字段访问的上下文中;即A 是一个类名。
JLS 中的语法是这样说的:
Primary:
ParExpression
NonWildcardTypeArguments (
ExplicitGenericInvocationSuffix | this Arguments)
this [Arguments]
super SuperSuffix
Literal
new Creator
Identifier { . Identifier }[ IdentifierSuffix]
BasicType {[]} .class
void.class
请注意,field 或 method 没有等效的语法。
(旁白:语法允许b.field,但JLS 声明b.field 表示名为“field”的字段的内容......如果不存在这样的字段,则编译错误。@987654328 同上@,加上一个 field c 必须存在。所以这些结构都不是你想要的意思......)
为什么存在这个限制?好吧,我猜是因为 Java 语言设计者没有看到需要混淆语言语法/语义来支持对 Field 和 Method 对象的方便访问。 (请参阅下面的 * 了解更改 Java 以允许您想要的一些问题。)
Java 反射的设计并不易于使用。在 Java 中,最好的做法是尽可能使用静态类型。它更有效,更不脆弱。将反射的使用限制在静态类型根本不起作用的少数情况。
如果您习惯于使用一种一切都是动态的语言进行编程,这可能会让您感到厌烦。但你最好不要与之抗争。
类文字有什么用途如此普遍吗?
我想,他们在课堂上支持这个的主要原因是它避免了每次你需要反思地做某事时调用Class.forName("some horrible string") 的程序。您可以将其称为对反射可用性的妥协/小让步。
我猜另一个原因是<type>.class 语法没有破坏任何东西,因为class 已经是一个关键字。 (IIRC,语法是在 Java 1.1 中添加的。)
* 如果语言设计者试图改进对这种东西的支持,就会出现各种各样的问题:
method 和field 是否变成关键字,这些更改无疑会破坏现有代码。b.field 视为隐式对象属性,因为它不适用于对象。而b.field 需要应用于字段/属性标识符。但是除非我们将field 设为保留字,否则我们会遇到异常情况,即您可以创建一个名为field 的字段,但您不能在Java 源代码中引用它。c.method,有一个问题是可以有多个可见的方法称为c。第二个问题是,如果有一个名为c 的字段和一个名为c 的方法,那么c.method 可能 是对一个名为method 的字段 的引用在c 字段引用的对象上。【讨论】:
class不是标识符。
我认为您需要此信息用于日志记录等。最不幸的是,尽管编译器可以完全访问此类信息,但无法获得此类信息。
只要有一点创意,你就可以使用反射来获取信息。我无法提供任何示例,因为几乎没有要求遵循,而且我没有心情完全浪费我的时间:)
【讨论】:
我不确定我是否完全理解您的问题。您不清楚A.class 语法的含义。您可以使用反射 API 通过以下方式从给定对象中获取类:
A a = new A()
Class c = a.getClass()
或
Class c = A.class;
然后使用c做一些事情。
反射 API 主要用于调试工具,由于 Java 支持多态,所以在运行时总是可以知道对象的实际 Class,因此开发了反射 API 来帮助调试问题(给定子类,当超类行为是预期的,等等)。
之所以没有 b.field 或 c.method,是因为它们在 Java 中没有意义,也没有功能用途。您不能创建对方法的引用,并且字段不能在运行时更改其类型,这些东西是在编译时设置的。 Java 是一种非常僵化的语言,在运行时灵活性方面没有太多优势(除非您使用动态类加载,但即便如此,您也需要一些有关加载对象的信息)。如果您来自 Ruby 或 Javascript 等灵活的语言,那么您可能会发现 Java 有点控制您的口味。
但是,让编译器帮助您找出代码中的潜在问题非常有帮助。
【讨论】:
.class 语法用于类名,而不是变量名。
在 java 中,并非一切都是对象。
你可以拥有
A a = new A()
Class cls = a.getClass()
或直接从课堂上
A.class
这样你就得到了类的对象。
通过反射,您可以获得方法和字段,但这会变得复杂。因为不是一切都是对象。这不是像 Scala 或 Ruby 那样一切都是对象的语言。 反射教程:http://download.oracle.com/javase/tutorial/reflect/index.html
顺便说一句:你没有指定 public/private/protected ,所以默认情况下你的东西被声明为包私有。这是包级保护访问http://download.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
【讨论】:
.class 语法用于类名,而不是变量名。