【问题标题】:Executing a generic Java class [duplicate]执行通用Java类[重复]
【发布时间】:2020-02-23 23:17:42
【问题描述】:

我的理解是,泛型 Java 类需要在类型上进行参数化,然后才能投入使用。我很惊讶下面的示例代码,其中泛型类没有被参数化,执行没有任何错误。

public class Box<T> {
  private T t;

  public static void main(String[] args) {
    System.out.println("It actually executed!!!!");
  }

  public void set(T t) { this.t = t; }

  public T get() { return t; }
}  

java Box
产生输出
It actually executed!!!!

在这种情况下,是否有隐式类型被传递给泛型类?

【问题讨论】:

  • 为什么它不起作用? main 是一个不引用 T 的方法。通常情况下,您不会有一个泛型类定义的主入口点,所以这个例子有点做作。
  • 没有你的类的实例,所以没有任何东西被分配给类型参数。

标签: java generics


【解决方案1】:

您不是“在这里执行课程”。而是在类中执行静态 main() 方法。允许您这样做的原因是类型擦除。仅在编译代码时才强制执行泛型类型。如果不声明任何引用或创建Box&lt;T&gt; 的实例,编译器就没有理由检查泛型类型。当你运行程序时,由于类型擦除,解释器对泛型代码一无所知,所以它愉快地运行程序。

【讨论】:

    【解决方案2】:

    Java 与 C 不同。“对于每个可以想象的 T / 每个在代码库中实际使用的 T,都没有一个 Box 类的变体”。只有盒子。例如:

    new Box<String>().getClass() == new Box<Integer>().getClass() // this is true
    

    您不会加载单独的类等。

    这确实有一些副作用:从 Box&lt;?&gt; x = new Box&lt;String&gt;(); 中的变量 x 派生 String不可能 - 该信息现在已经消失了。这称为擦除。

    实际上,泛型几乎完全是编译器的想象。这就像在打字稿中输入信息等:它在编译时就在那里,编译器会使用它来告诉你类型不对齐(编译器错误),但是一旦编译器接受它,它就会被编译成一种在运行时完全消除此信息的形式*。

    这就是为什么上面的代码可以正常工作的原因:你有一个静态方法,&lt;T&gt; 甚至在这里都不存在。

    让我们放大您的 get 和 set 方法以及 't' 字段:如果他们阅读:private Object t; public void set(Object o) { this.t = t; } public Object get() { return t; },只有一个例外:如果,在编译时,它不会'没有意义,编译器将拒绝编译它。此外,对于get 调用的任何调用者,都会默默地包含一个演员表。

    *) 不完全; signatures 中泛型的任何使用,因此,字段的类型、您的类自己的定义、您的 implementsextends 行,以及您的方法的返回类型或参数类型,是没有被淘汰,但好像是对VM的评论:VM不关心这些东西;它的存在只是为了,如果您在类路径上使用一些东西调用javac,那么 javac 在与这些成员交互时知道泛型是什么。就是这样。

    【讨论】:

      【解决方案3】:

      Java 中的泛型旨在为您的对象提供编译时类型安全。 对于您的情况,它根本没有在类中的 main() 方法中访问 t 变量。所以java会愉快地编译和运行程序。

      如果您在main() 方法中实例化Box 类:

      Box b = new Box(); // this is will produce "unsafe operations" note
      b.set("this is the string");
      System.out.println(b.get().getClass().getName());
      

      根据最后的变量赋值,你会在这里得到一个字符串。

      【讨论】:

        【解决方案4】:

        您要询问的方法是main(),如下所示(我将类和方法编辑到最低限度)。如您所见,它有效。

        class Box<T> {
            public static void main(String[] args) {
                // do something
            }
        }
        

        该方法很好,因为它与泛型类型T 没有关联。这里,main 是一个静态方法,这意味着它在调用时没有一个对象实例。

        泛型不适用于静态方法(或字段),但您上面的示例并没有这样做。这是一个不同的示例,它尝试定义一个也使用T 的静态方法(但它不起作用):

        class Box<T> {
            public static void bar(T args) {
                // do something
            }
        }
        

        这个示例是一个静态方法,将在没有对象实例的情况下调用 - 像这样:Box.bar(someArgs) - 但这是无效的。如果不首先创建具有某种泛型类型的 Box 的实例,编译器如何知道 T 是什么?

        可以定义一个静态方法来使用泛型类型,但在您的示例中完全独立于T。对于一些单独的泛型类型X,这是一种方法:

        class Box<T> {
            public static <X> void foo(X input) {
                // do something
            }
        }
        

        你可以这样称呼它:Box.foo("");

        【讨论】:

          猜你喜欢
          • 2019-02-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-18
          • 1970-01-01
          • 2018-05-10
          相关资源
          最近更新 更多