简体   繁体   English

Java泛型。 为什么编译?

[英]Java Generics. Why does it compile?

abstract class Type<K extends Number> {
    abstract <K> void use1(Type<K> k);           // Compiler error (Type parameter K is not within its bounds)
    abstract <K> void use2(Type<? extends K> k); // fine
    abstract <K> void use3(Type<? super K> k);   // fine
}

The method generic type K shadows the class generic type K, so <K> doesn't match <K extends Number> in use1() .The compiler doesn't know anything usefull about new generic type <K> in use2() and use3() but it is still legal to compile . 方法泛型类型K隐藏类泛型类型K,因此<K>use1()中的<K extends Number>不匹配。编译器对use2()新泛型类型<K>一无所知。 use3()但编译仍然合法。 Why <? extends K> 为什么<? extends K> <? extends K> (or <? super K> ) match <K extends Number> ? <? extends K> (或<? super K> )匹配<K extends Number>

The problem you have is that There are two K types. 你遇到的问题是有两种K类型。 It may be clearer if you rename one. 如果重命名,可能会更清楚。

abstract class Type<N extends Number> {
    abstract <K extends Number> void use1(Type<K> k); // fine
    abstract <K> void use2(Type<? extends K> k); // fine
    abstract <K> void use3(Type<? super K> k);   // fine
}

There are cases where you have to provide duplicate information the compiler can infer, and other places where you don't. 在某些情况下,您必须提供编译器可以推断的重复信息,以及其他您不能提供的信息。 In Java 7 it has added a <> diamond notation to tell the compiler to infer types it didn't previously. 在Java 7中,它添加了一个<>菱形符号,告诉编译器推断它以前没有的类型。


To illustrate what I mean. 说明我的意思。 Here is different ways to create an instance of a generic class. 以下是创建泛型类实例的不同方法。 Some requires the type be given twice, others only once. 有些要求给出两次类型,其他只需要一次。 The compiler can infer the type. 编译器可以推断出类型。

In general, Java doesn't infer types when it might do in most other languages. 通常,Java不会在大多数其他语言中推断类型。

class Type<N extends Number> {
    private final Class<N> nClass;

    Type(Class<N> nClass) {
        this.nClass = nClass;
    }

    static <N extends Number> Type<N> create(Class<N> nClass) {
        return new Type<N>(nClass);
    }

    static void main(String... args) {
      // N type is required.
      Type<Integer> t1 = new Type<Integer>(Integer.class);

      // N type inferred in Java 7.
      Type<Integer> t2 = new Type<>(Integer.class); 

      // type is optional
      Type<Integer> t3 = Type.<Integer>create(Integer.class); 

      // type is inferred
      Type<Integer> t4 = create(Integer.class);
    }

When you define the method like this: 当您定义这样的方法时:

abstract <K> void use1(Type<K> k);

You're effectively hiding the type K in your class definition. 您实际上是在类定义中隐藏类型K You should be able to define the methods like this: 您应该能够定义这样的方法:

abstract void use1(Type<K> k);

First of all, let's rewrite it to avoid shadowing: 首先,让我们重写它以避免阴影:

abstract class Type<N extends Number> {
    abstract <K> void use1(Type<K> k); 
    abstract <K> void use2(Type<? extends K> k); 
    abstract <K> void use3(Type<? super K> k);   
}

In the first method K acts as a type parameter of Type<N extends Number> , thus its value sould comply to the bound of Type 's N . 在第一种方法中, K充当Type<N extends Number>的类型参数,因此其值应符合TypeN的界限。 However, method declaration doesn't have any restrictions on value of K , therefore it's not legal. 但是,方法声明对K值没有任何限制,因此它不合法。 It would be legal if you add a necessary restriction on K : 如果你对K添加必要的限制,那将是合法的:

abstract <K extends Number> void use1(Type<K> k);

In the following methods, the actual type parameter of Type is unknown ( ? ), and K imposes additional bound on it, so that there is nothing illegal in these declarations. 在下面的方法中, Type的实际类型参数是未知的( ? ),并且K施加了额外的限制,因此在这些声明中没有任何违法行为。

Here is a more practical example with the similar declarations: 这是一个更实际的例子,具有类似的声明:

class MyList<N extends Number> extends ArrayList<N> {}

<K> void add1(MyList<K> a, K b) {
     a.add(b); // Given the method declaration, this line is legal, but it 
          // violates type safety, since object of an arbitrary type K can be
          // added to a list that expects Numbers
          // Thus, declaration of this method is illegal
}

<K> void add2(MyList<? extends K> a, K b) {
     // a.add(b) would be illegal inside this method, so that there is no way
     // to violate type safety here, therefore declaration of this method is legal
}

<K> void add3(MyLisy<? super K> a, K b) {
     a.add(b); // This line is legal, but it cannot violate type safey, since
          // you cannot pass a list that doesn't expect K into this method
}

This is a gray area; 这是一个灰色区域; javac 7 and 6 disagree; javac 7和6不同意; JLS3 is outdated; JLS3已经过时了; no idea where's the new spec. 不知道新规格在哪里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM