【问题标题】:why MyClass.class exists in java and MyField.field isn't?为什么 MyClass.class 存在于 java 中而 MyField.field 不存在?
【发布时间】:2010-12-19 23:58:06
【问题描述】:

假设我有:

class A {
    Integer b;
    void c() {}
}

为什么Java有这样的语法:A.class,却没有这样的语法:b.field,c.method

类文字有什么用途如此普遍吗?

【问题讨论】:

    标签: java syntax


    【解决方案1】:

    A.class 语法看起来 像一个字段访问,但实际上它是一个特殊语法规则的结果,在一个根本不允许正常字段访问的上下文中;即A 是一个类名。

    JLS 中的语法是这样说的:

    Primary:
      ParExpression
            NonWildcardTypeArguments (
                ExplicitGenericInvocationSuffix | this Arguments)
      this [Arguments]
      super SuperSuffix
      Literal
      new Creator
      Identifier { . Identifier }[ IdentifierSuffix]
      BasicType {[]} .class
      void.class
    

    请注意,fieldmethod 没有等效的语法。

    (旁白:语法允许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 中添加的。)


    * 如果语言设计者试图改进对这种东西的支持,就会出现各种各样的问题:

    • 这些更改会在语言中引入歧义,使编译和其他依赖于解析器的任务变得更加困难。
    • 无论methodfield 是否变成关键字,这些更改无疑会破坏现有代码。
    • 您不能将b.field 视为隐式对象属性,因为它不适用于对象。而b.field 需要应用于字段/属性标识符。但是除非我们将field 设为保留字,否则我们会遇到异常情况,即您可以创建一个名为field 的字段,但您不能在Java 源代码中引用它。
    • 对于c.method,有一个问题是可以有多个可见的方法称为c。第二个问题是,如果有一个名为c 的字段和一个名为c 的方法,那么c.method 可能 是对一个名为method字段 的引用在c 字段引用的对象上。

    【讨论】:

    • '这是一个特殊语法规则的结果,在一个根本不允许正常字段访问的上下文中;即其中 A 是一个类名'。这是不真实的。如果 A 有静态字段或方法,您可以使用该语法访问它们。
    • 问题是class不是标识符。
    【解决方案2】:

    我认为您需要此信息用于日志记录等。最不幸的是,尽管编译器可以完全访问此类信息,但无法获得此类信息。

    只要有一点创意,你就可以使用反射来获取信息。我无法提供任何示例,因为几乎没有要求遵循,而且我没有心情完全浪费我的时间:)

    【讨论】:

    【解决方案3】:

    我不确定我是否完全理解您的问题。您不清楚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 有点控制您的口味。

    但是,让编译器帮助您找出代码中的潜在问题非常有帮助。

    【讨论】:

    • 您的代码不是有效的 Java。 .class 语法用于类名,而不是变量名。
    • 你是对的,它的 .getClass() 对象。我已经适当地更新了代码。
    【解决方案4】:

    在 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

    【讨论】:

    • 不是真的。如果您不指定修饰符(private/protected/public),它将使用默认修饰符,也称为包范围。包宽/默认意味着同一个包中的任何东西都可以扩展/访问它,但包之外的任何东西都不能。
    • @Codemwnci 哦,是的,对不起,我没有做足够的 java 那些时间来记住那些东西谢谢
    • @Codemwnci 规范中的“默认访问”,俗称“包私有”。
    • 您的代码不是有效的 Java。 .class 语法用于类名,而不是变量名。
    • @Laurence:这值得否决吗?只是一个更正,没关系
    猜你喜欢
    • 1970-01-01
    • 2019-02-21
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 2014-03-13
    • 1970-01-01
    • 2012-05-21
    相关资源
    最近更新 更多