泛型 – 为什么类类型变量在静态上下文中无效?

huangapple go评论70阅读模式
英文:

Generics - Why are Class type variables not valid in static contexts?

问题

我正在从一本书中学习Java泛型。该书指出:“类类型变量在静态上下文中无效”,并通过以下示例进行了解释。

考虑一个带有类型变量的泛型类,例如Entry<K,V>。您不能在静态变量或方法中使用类型变量K和V。例如,以下代码无法正常工作:

public class Entry&lt;K, V&gt; {
    // 编译错误 - 静态上下文中的V(无法从静态上下文中引用“Entry.this”)
    private static V defaultValue;

    // 编译错误 - 静态上下文中的V(无法从静态上下文中引用“Entry.this”)
    public static void setDefault(V value) {
        defaultValue = value;
    }
}

毕竟,类型擦除意味着在被擦除的Entry类中只有一个这样的变量或方法,而不是每个K和V都有一个。

我不理解上述解释。我尝试创建了与K相同的代码,但我得到了相同的编译错误。为什么上述代码是非法的?

英文:

I am learning Java generics from a book. The book says that "Class Type Variables Are Not Valid in Static Contexts" and explains it with the following example.

Consider a generic class with type variables, such as Entry<K, V>. You cannot use the type variables K and V with static variables or methods. For example, the following does not work:

public class Entry&lt;K, V&gt; {
    // Compiler error - V in static context (&quot;Entry.this&#39; cannot be referenced from a static context&quot;)
    private static V defaultValue;

    // Compiler error - V in static context (&quot;Entry.this&#39; cannot be referenced from a static context&quot;)
    public static void setDefault(V value) {
        defaultValue = value;
    }
}

After all, type erasure means there is only one such variable or method in the erased Entry class, and not one for each K and V.

I don't understand the above explanation. I tried to create the same code for K also and I got the same compile errors. Why is the above code illegal ?

答案1

得分: 5

假设static变量按照您描述的方式工作...

我本应该能够这样做:

Entry<String, Integer>.defaultValue = 1;
Entry<String, String>.defaultValue = "Hello";
System.out.println(Entry<String, Integer>.defaultValue); // 1
System.out.println(Entry<String, String>.defaultValue); // Hello

这正是您期望的,不是吗?

但请记住,static变量是“每个类一个”。由于类型擦除,Entry<String, Integer>Entry<String, String>被视为同一类。对于上述代码工作,1"Hello"需要存储在_两个_不同的位置(两个变量)!

英文:

Let's suppose static variables did work the way you described...

I would've been able to do:

Entry&lt;String, Integer&gt;.defaultValue = 1;
Entry&lt;String, String&gt;.defaultValue = &quot;Hello&quot;;
System.out.println(Entry&lt;String, Integer&gt;.defaultValue); // 1
System.out.println(Entry&lt;String, String&gt;.defaultValue); // Hello

That's what you'd expect, isn't it?

But remember that static variables are one per class. Because of type erasure, Entry&lt;String, Integer&gt; and Entry&lt;String, String&gt; are considered the same class. And for the above code the work, 1 and &quot;Hello&quot; would need to be stored at two different places (two variables)!

答案2

得分: 3

V将是实例化类的类型。如果两个类实例化的时候,使用不同的类型来实例化V,那么用于访问在类级别上由所有类共享的静态字段的类型会是哪种类型?

private static V defaultValue;

现在,一个被实例化的类将V设置为String,另一个类将V设置为Long

但静态字段可供所有类使用,因此在访问时会使用哪种类型?

这与为什么不能从静态上下文引用实例字段有关。一旦进入静态上下文,将访问哪个“实例”字段的实例?无法确定,因此这是不合法的(也没有意义)。

英文:

V will be the type of the instantiating class. If two classes instantiate with different types for V, which type would be used to access the static field which is shared by all classes at the class level?

 private static V defaultValue;

Now one instantiated class has V as String and another has V as Long.

But static is available to all classes so which one would it be when accessed?

It's related as to why you can't reference an instance field from a static context. Once inside a static context, which "instance" of the instance field would be accessed? It can't be determined so it isn't legal (and doesn't make sense).

答案3

得分: 2

Java泛型的设计者选择使用一种称为“类型擦除”的机制来实现它。这意味着像Entry<String,Integer>Entry<Integer,String>这样的泛型特化并不存在于单独的类中。类型参数被擦除。

在从Entry<String,Integer>Entry<Integer,String>中擦除类型参数之后,你只剩下了Entry类。

如果有可能像defaultValue这样的静态变量,你会期望Entry<String,Integer>.defaultValue是一个整数。并且你会期望Entry<Integer,String>.defaultValue是一个字符串。但是在类型擦除之后,只剩下一个Entry类,其中只有一个defaultValue变量,现在必须既是整数又是字符串。这是不可能的。这就是为什么你不能有一个泛型类型的静态变量。

英文:

Designers of Java generics chose to implement it using a mechanism called "type erasure". It means that generic specializations like Entry&lt;String,Integer&gt; and Entry&lt;Integer,String&gt; do not exist as separate classes. The type parameters are erased.

After you erase the type parameters from Entry&lt;String,Integer&gt; and Entry&lt;Integer,String&gt; you're left with just the Entry class.

If it were possible to have a static variable like defaultValue you would expect Entry&lt;String,Integer&gt;.defaultValue to be a Integer. And you would expect Entry&lt;Integer,String&gt;.defaultValue to be a String. But after type erasure only one Entry class with only one defaultValue variable, which now has to be both Integer and String. That's impossible. That's why you can't have a static variable of the generic type.

huangapple
  • 本文由 发表于 2020年4月4日 02:15:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/61018031.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定