简体   繁体   English

了解上界和下界 ? 在 Java 泛型中

[英]Understanding upper and lower bounds on ? in Java Generics

I am really having a tough time understanding the wild card parameter.我真的很难理解通配符参数。 I have a few questions regarding that.我有几个问题。

  1. ? as a type parameter can only be used in methods.作为类型参数只能在方法中使用。 eg: printAll(MyList<? extends Serializable>) I cannot define classes with ?例如: printAll(MyList<? extends Serializable>)我不能用? as type parameter.作为类型参数。

  2. I understand the upper bound on ?我了解上限? . . printAll(MyList<? extends Serializable>) means: " printAll will print MyList if it has objects that implement the Serialzable interface. " printAll(MyList<? extends Serializable>)手段:“ printAll将打印MyList如果它具有实施该对象Serialzable接口”。
    I have a bit of an issue with the super .我对super有点问题。 printAll(MyList<? super MyClass>) means: " printAll will print MyList if it has objects of MyClass or any class which extends MyClass (the descendants of MyClass ). " printAll(MyList<? super MyClass>)手段:“ printAll将打印MyList如果它具有的对象MyClass或延伸的任何类MyClass (的后代MyClass )”。

Correct me where I went wrong.纠正我哪里出错了。

In short, only T or E or K or V or N can be used as type parameters for defining generic classes.简而言之,只有TEKVN可以用作定义泛型类的类型参数。 ? can only be used in methods只能在方法中使用


Update 1: 更新 1:

 public void printAll(MyList<? super MyClass>){ // code code code }

Accordint to Ivor Horton's book, MyList<? super MyClass>根据 Ivor Horton 的书MyList<? super MyClass> MyList<? super MyClass> means that I can print MyList if it has objects of MyClass or any of the interfaces or classes it implements. MyList<? super MyClass>表示如果MyList具有MyClass对象或它实现的任何接口或类,我可以打印它。 That is, MyClass is a lower bound .也就是说, MyClass是一个下界 It is the last class in the inheritance hierarchy.它是继承层次结构中的最后一个类。 This means my initial assumption was wrong.这意味着我最初的假设是错误的。

So, say if MyClass looks like:所以,假设MyClass看起来像:

 public class MyClass extends Thread implements ActionListener{ // whatever }

then, printAll() will print if然后, printAll()将打印如果
1. There are objects of MyClass in the list 1.列表中有MyClass对象
2. There are objects of Thread or ActionListener in the List 2.有对象ThreadActionListenerList


Update 2: 更新 2:

So, after having read the many answers to the question, here is my understanding:因此,在阅读了该问题的许多答案后,这是我的理解:

  1. ? extends T ? extends T means any class which extends T . ? extends T表示任何扩展T Thus, we are referring to the children of T .因此,我们指的T孩子 Hence, T is the upper bound.因此, T是上限。 The upper-most class in the inheritance hierarchy继承层次结构中最上层的类

  2. ? super T ? super T means any class / interface which is super of T . ? super T任何类/接口是superT Thus we are referring to all the parents of T .因此,我们指的T所有父母 T is thus the lower bound.因此, T是下限。 The lower-most class in the inheritance hierarchy继承层次结构中最底层的类

? as a type parameter can only be used in methods.作为类型参数只能在方法中使用。 eg: printAll(MyList<? extends Serializable>) I cannot define classes with ?例如: printAll(MyList<? extends Serializable>)我不能用? as type parameter.作为类型参数。

A wildcard ( ? ) isn't a formal type parameter, but rather can be used as a type argument .通配符 ( ? ) 不是正式的类型参数,而是可以用作类型参数 In the example you give, ? extends Serializable在你给出的例子中, ? extends Serializable ? extends Serializable is given as a type argument to the generic type MyList , of the printAll method's parameter. ? extends Serializable被给定为一个类型参数的一般类型MyList ,所述的printAll方法的参数。

Methods can also declare type parameters like classes, for example:方法也可以像类一样声明类型参数,例如:

static <T extends Serializable> void printAll(MyList<T> myList)

I understand the upper bound on ?我了解上限? . . printAll(MyList<? extends Serializable>) means printAll will print MyList if it has objects that implement the Serialzable interface printAll(MyList<? extends Serializable>)表示如果具有实现 Serialzable 接口的对象,printAll 将打印 MyList

More accurately, it means a call to printAll will compile only if it is passed a MyList with some generic type that is or implements Serializable .更准确地说,它意味着一个呼叫printAll如果它传递一个只会编译MyList有一些通用的类型,它是或实现Serializable In this case it would accept a MyList<Serializable> , MyList<Integer> , etc.在这种情况下,它将接受MyList<Serializable>MyList<Integer>等。

I have a bit of an issue with the super .我对super有点问题。 printAll(MyList<? super MyClass>) means printAll will print MyList if it has objects of MyClass or any class which extends MyClass (the descendants of MyClass) printAll(MyList<? super MyClass>)表示printAll 将打印 MyList 如果它有 MyClass 的对象或任何扩展 MyClass 的类MyClass的后代)

A wildcard bounded with super is a lower bound.super为界的通配符是下界 So we could say a call to printAll will compile only if it is passed a MyList with some generic type that is MyClass or some super-type of MyClass .因此,我们可以说一个呼叫printAll如果它传递一个只会编译MyList有一些通用的类型为MyClass或一些超级型MyClass So in this case it would accept MyList<MyClass> , eg MyList<MyParentClass> , or MyList<Object> .因此,在这种情况下,它将接受MyList<MyParentClass> MyList<MyClass> ,例如MyList<MyParentClass>MyList<MyParentClass> MyList<Object>

So, say if MyClass looks like:所以,假设 MyClass 看起来像:

 public class MyClass extends Thread implements ActionListener{ // whatever }

then, printAll() will print if然后,printAll() 将打印如果

  1. There are objects of MyClass in the list列表中有 MyClass 的对象
  2. There are objects of Thread or ActionListener in the list列表中有 Thread 或 ActionListener 的对象

You're on the right track.你在正确的轨道上。 But I think saying eg "it will print if there are objects of MyClass in the list" is problematic.但是我认为说例如“如果列表中有MyClass对象,它就会打印”是有问题的。 That makes it sound like you're defining runtime behavior - generics are all about compile time checks.这听起来像是在定义运行时行为 - 泛型都是关于编译时检查的。 For example wouldn't be able to pass a MyList<MySubclass> as an argument for MyList<? super MyClass>例如,不能将MyList<MySubclass>作为参数传递给MyList<MySubclass> MyList<? super MyClass> MyList<? super MyClass> , even though it might contain instances of MyClass , by inheritance. MyList<? super MyClass> ,即使它可能包含MyClass实例,通过继承。 I would reword it to:我将其改写为:

A call to printAll(MyList<? super MyClass>) will compile only if it is passed a:printAll(MyList<? super MyClass>)的调用只有在传递了以下参数时才会编译:

  1. MyList<MyClass>
  2. MyList<Thread>
  3. MyList<Runnable>
  4. MyList<ActionListener>
  5. MyList<EventListener>
  6. MyList<Object>
  7. MyList<? super X> MyList<? super X> where X is MyClass , Thread , Runnable , ActionListener , EventListener , or Object . MyList<? super X>其中XMyClassThreadRunnableActionListenerEventListenerObject

So, after having read the many answers to the question, here is my understanding:因此,在阅读了该问题的许多答案后,这是我的理解:

? extends T ? extends T means any class which extends T . ? extends T表示任何扩展 T 的类 Thus, we are referring to the children of T. Hence, T is the upper bound.因此,我们指的是 T 的孩子。因此, T 是上限。 The upper-most class in the inheritance hierarchy继承层次结构中最上层的类

? super T ? super T means any class / interface which is super of T. Thus we are referring to all the parents of T. T is thus the lower bound. ? super T指任何类/接口是super T.因此我们指的是所有T. T的父母因此下界。 The lower-most class in the inheritance hierarchy继承层次结构中最底层的类

Close, but I wouldn't say "children of T " or "parents of T ", since these bounds are inclusive - it would be more accurate to say " T or its subtypes", and " T or its supertypes".关闭,但我不会说“ T孩子”或“ T父母”,因为这些界限是包含性的——说“ T或其子类型”和“ T或其超类型”会更准确。

First of all T or E or K or whatever are not fixed names.首先, TEK或其他任何不是固定名称。 They are just type variables, and you decide the name for them.它们只是类型变量,由您决定它们的名称。 T , E , K are just examples but you could call it Foo or whatever. TEK只是示例,但您可以将其称为Foo或其他名称。

Now going to your first question: since the wildcard ?现在去你的第一个问题:因为通配符? represents the "any and unknown" type, the unspecified one, it doesn't make any sense to declare a class generic over an unspecified type.表示“任何和未知”类型,未指定的类型,在未指定的类型上声明泛型类没有任何意义。 It's useful to have wildcard in parameters of methods or in variables when you don't care about the type.当您不关心类型时,在方法的参数或变量中使用通配符很有用。

Now regarding your second question: the lower bound gives even more flexibility to your generic methods.现在关于您的第二个问题:下限为您的通用方法提供了更大的灵活性。 both extends and super are the opposite: extendssuper都是相反的:

  • ? extends T ? extends T : an unknown type which is a subtype of T ? extends T :未知类型,它是T的子类型
  • ? super T ? super T : an unknown type which is a super type of T ? super T :未知类型,它是T的超类型

The latter can be useful when you want to accept a type that is compatible with T (so that T is-a that type).当您想要接受与 T 兼容的类型(以便 T 是该类型)时,后者会很有用。 A practical example can be found here .可以在此处找到一个实际示例。

Lets start from the beginning.让我们从头开始。

Strictly speaking any valid java identifier can be used as a generic type parameter - it is just a special type of variable:严格来说,任何有效的 java 标识符都可以用作泛型类型参数——它只是一种特殊类型的变量:

public static final class MyGenericClass<MyGenericType> {

}

Is perfectly valid Java.是完全有效的 Java。

Next, you can use ?接下来,您可以使用? anywhere where you can make a declaration.任何可以声明的地方。 You can use the wildcard when you declare variables but not when you instantiate them:您可以在声明变量时使用通配符,但不能在实例化它们时使用:

public static final class MyGenericClass {
    private final Collection<? extends String> myThings;

    public MyGenericClass(Collection<? extends String> myThings) {
        this.myThings = myThings;
    }  

    public void doStuff(final Collection<? extends String> myThings) {

    }
}

Is again all valid, you cannot do this:再次全部有效,您不能这样做:

final Collection<? extends String> myThings = new ArrayList<? extends String>();

When it comes to extends vs super this is called co-variance vs contra-variance.当涉及到extendssuper这被称为协方差与逆变。 It determines which direction along the class hierarchy supplied types are allowed to travel:它确定允许提供的类型沿类层次结构的哪个方向行进:

final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();

The first two examples demonstrate extends - the tightest bound you can assume from the Collection is Runnable as the user can pass a Collection of anything that has Runnable in its inheritance hierarchy.前两个示例演示了extends - 您可以从Collection假设的最紧密的界限是Runnable因为用户可以传递在其继承层次结构中具有Runnable的任何东西的Collection

The second two example demonstrate super - the tightest bound you can assume from the Collection is Object as we allow anything that is in the inheritance hierarchy of Runnable .第二个两个示例演示super -最严格的约束,你可以从假设CollectionObject ,因为我们允许任何处于继承层次Runnable

For the first question: you can not define a method with ?对于第一个问题:你不能用? as a type parameter either.作为类型参数。 The following will not compile:以下不会编译:

void <?> foo() {}

? is used for binding to another generics without providing the type parameter.用于绑定到另一个泛型而不提供类型参数。 You can write for methods:您可以为方法编写:

void foo(List<?> e) {}

And you can also write for classes:你也可以为类编写:

public class Bar<E extends List<?>> { }

For the use of super :对于super的使用:

public void printAll(MyList<? super MyClass>){
    // code code code
}

This will not as you say print the list "if it has objects of MyClass".这不会像你说的那样打印列表“如果它有 MyClass 的对象”。 It can have objects of any class that is a subclass of a class that is a parent of MyClass.它可以拥有作为 MyClass 父类的子类的任何类的对象。 The compiler does not know at compile time what are the objects that will be in the list anyway.编译器在编译时并不知道列表中的对象是什么。

To get your head around it, consider a simple example with the Number class hierarchy.为了解决这个问题,请考虑一个带有Number类层次结构的简单示例。 Float and Integer are children of Number . FloatIntegerNumber孩子。 You can write your method like this:你可以这样写你的方法:

public void printAll(List<? super Float>){
    // code code code
}

Then you can call that method with a List<Number> :然后您可以使用List<Number>调用该方法:

List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.

This is possible would not be super useful in that case.在这种情况下,这可能不会非常有用。 Where it would be useful for example is when you want to add Float to a collection without wanting it to be only a List, like:例如,当您想将Float添加到集合而不希望它只是一个列表时,它会很有用,例如:

public void addFloat(List<? super Float> list){
    list.add(2.5);
}

Lower-bound says: You can use the class mentioned after 'super' keyword, or any of its super types.下限表示:您可以使用在 'super' 关键字之后提到的类,或其任何超类型。 This can get tricky.这可能会变得棘手。 Those super types may have other (completely different) classes inheriting from them, as below example shows.这些超类型可能有其他(完全不同的)类继承自它们,如下例所示。

Say,说,

List<? super Car> cars = new ArrayList <Vehicle>();

Should the programmer be allowed to write:是否允许程序员编写:

cars.add(new Helicopter()); //Helicopter is a kind of Vehicle

That should obviously not be allowed, and it reflects a danger in using lower-bounds.这显然不应该被允许,它反映了使用下限的危险。

Programmer should be allowed to add a Vehicle to list, but not ANY Vehicle.应该允许程序员将车辆添加到列表中,但不能添加任何车辆。 He must cast it, to let Java know he is only adding a Car Vehicle after all, like this:他必须投射它,让 Java 知道他毕竟只是添加了一辆 Car Vehicle,就像这样:

cars.add((Car) new Vehicle()); 

Hmmmm your statement on super ( printAll(MyList<? super MyClass>) ) is not clear.嗯,您对super ( printAll(MyList<? super MyClass>) ( printAll(MyList<? super MyClass>) ) 的声明不清楚。 What it means, assuming Myclass extends Object is that you can printAll(MyList<MyClass>) and you can printAll(MyList<Object>) but nothing else.... it means that the generic type of MyList has to be a superclass (not a subclass) of MyClass.这意味着,假设 Myclass 扩展Object是你可以printAll(MyList<MyClass>)并且你可以printAll(MyList<Object>)但没有别的......这意味着printAll(MyList<Object>)的泛型类型必须是一个超类(不是 MyClass 的子类)。 This is different to what you said.这和你说的不一样。

As for the T, E, K, V, or N, well, these are meaningless names in and of themselves.至于 T、E、K、V 或 N,好吧,它们本身就是毫无意义的名称。 You can use anything you want.你可以使用任何你想要的东西。 Convention suggests single-letter upper-case values though, and T is often used for generic methods, and E for classes....约定建议使用单字母大写值,T 通常用于泛型方法,E 用于类......

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

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