简体   繁体   English

多级泛型类型中的Java通配符

[英]Java wildcard in multi-level generic type

Why Bar.go is OK with argument f2 but not with argument f1 ? 为什么Bar.go OK使用参数f2而不是参数f1

public class HelloWorld {
    public static void main(String[] args) {
        Foo<Foo<?>> f1 = new Foo<Foo<?>>();
        Foo<Foo<String>> f2 = new Foo<Foo<String>>();
        Bar.go(f1);     // not OK
        Bar.go(f2);     // OK
    }

    public static void p(Object o) {
        System.out.println(o);
    }
}

class Foo<E> {
}

class Bar {
    public static <T> void go(Foo<Foo<T>> f) {
    }
}

Shouldn't the compiler automatically infer type T as capture of ? 不应该编译器自动推断类型Tcapture of ? in both cases? 在这两种情况下?

Foo<Foo<?>> f1 = new Foo<Foo<?>>();

This implies that the type is unknown and objects of any type can be added to Foo<Foo<?>> that are heterogeneous and compiler cannot guarantee that all object in Foo<Foo<?>> are of same type. 这意味着类型是未知的,任何类型的对象都可以添加到异构的Foo<Foo<?>> ,编译器不能保证Foo<Foo<?>>中的所有对象都是相同的类型。 Hence it cannot be passed to Bar.go that takes a bounded type as parameter. 因此,它无法传递给采用有界类型作为参数的Bar.go

You can instead declare that as Foo<Foo<Object>> f1 = new Foo<Foo<Object>>(); 你可以改为声明为Foo<Foo<Object>> f1 = new Foo<Foo<Object>>(); to pass it to Bar.go where you explicitly mention everything is of type Object . 将它传递给Bar.go ,你明确提到一切都是Object类型。

Great question! 好问题!

(In the following comments, wrt a class generic in E like Foo< E > , define "covariant method" as a method that returns an E without having any parameters using E , and a "contravariant method" as the opposite: one which takes a formal parameter of type E but doesn't return a type involving E . [The real definition of these terms is more complicated, but never mind that for now.]) (在下面的评论,WRT一类通用的在EFoo< E >中,定义“协变方法”作为返回的方法E不使用具有任何参数E ,以及“逆变方法”为相反:一个这需要类型E的形式参数,但不返回涉及E的类型。[这些术语的真正定义更复杂,但现在不记得了。])

It seems that the compiler is trying to bind T to Object in the case of f1 , because if you do f1的情况下,似乎编译器试图将T绑定到Object ,因为如果你这样做的话

class Bar0 {
    public static < T > void go( Foo< Foo< ? extends T > > f ) {
        // can pass a Foo< T > to a contravariant method of f;
        // can use any result r of any covariant method of f,
        // but can't pass T to any contravariant method of r
    }
}

then the go(f1) works, but now go(f2) doesn't, because even though Foo< String > <: Foo< ? extends String > 然后go(f1)工作,但现在go(f2)没有,因为即使Foo< String > <: Foo< ? extends String > Foo< String > <: Foo< ? extends String > , that does not imply that Foo< Foo< String > > <: Foo< Foo< ? extends String > > Foo< String > <: Foo< ? extends String > ,这并不意味着Foo< Foo< String > > <: Foo< Foo< ? extends String > > Foo< Foo< String > > <: Foo< Foo< ? extends String > > . Foo< Foo< String > > <: Foo< Foo< ? extends String > >

Here are a few modifications that compile for both f1 and f2 : 以下是为f1f2编译的一些修改:

class Bar1 {
    public static < T > void go( Foo< ? super Foo< T > > f ) {
        // can't properly type the results of any covariant method of f,
        // but we can pass a Foo< T > to any contravariant method of f
    }
}

class Bar2 {
    public static < T > void go( Foo< ? extends Foo< ? extends T > > f ) {
        // can't pass a Foo< T > to a contravariant method of f;
        // can use result r of any covariant method of f;
        // can't pass a T to a contravariant method of r;
        // can use result of covariant method of r
    }
}

A Nice Read What do multi-level wildcards mean? 一个很好的阅读多级通配符是什么意思?

Example: 例:

Collection< Pair<String,Long> >        c1 = new ArrayList<Pair<String,Long>>();

Collection< Pair<String,Long> >        c2 = c1;  // fine
Collection< Pair<String,?> >           c3 = c1;  // error
Collection< ? extends Pair<String,?> > c4 = c1; // fine  

Of course, we can assign a Collection<Pair<String,Long>> to a Collection<Pair<String,Long>> . 当然,我们可以将Collection<Pair<String,Long>>分配给Collection<Pair<String,Long>> There is nothing surprising here. 这里没有什么可惊讶的。

But we can not assign a Collection<Pair<String,Long>> to a Collection<Pair<String,?>> . 但是我们不能将Collection<Pair<String,Long>>分配给Collection<Pair<String,?>> The parameterized type Collection<Pair<String,Long>> is a homogenous collection of pairs of a String and a Long ; 参数化类型Collection<Pair<String,Long>>是String和Long对的同类集合; the parameterized type Collection<Pair<String,?>> is a heterogenous collection of pairs of a String and something of unknown type. 参数化类型Collection<Pair<String,?>>是一组String和一些未知类型的异构集合。 The heterogenous Collection<Pair<String,?>> could for instance contain a Pair<String,Date> and that clearly does not belong into a Collection<Pair<String,Long>> . 异构Collection<Pair<String,?>>可以包含一个Pair<String,Date> ,它显然不属于Collection<Pair<String,Long>> For this reason the assignment is not permitted. 因此,不允许分配。

I will prove that if the compiler allows Bar.go(f1); 我将证明,如果编译器允许Bar.go(f1); , the type system(safety) would be broken: ,类型系统(安全)将被打破:

Java grammar allows you to use T as a type to declare variables in go() . Java语法允许您使用T作为类型来在go()声明变量。 Something like: T t = <something> . 类似的东西: T t = <something>

Now, let's use ArrayList instead of Foo , 现在,让我们使用ArrayList而不是Foo

Then we have: 然后我们有:

class HW {
 public static void main(String[] args) {
        ArrayList<ArrayList<?>> f1 = new ArrayList<ArrayList<?>>();
        go(f1);     // not OK
    }

    public static <T> void go(ArrayList<ArrayList<T>> f) {
    }
}

ArrayList<?> is supertype of ArrayList<String> , it is also supertype of ArrayList<Integer> , meaning that you can do the following in main : ArrayList<?>ArrayList<String>超类型,它也是ArrayList<Integer>超类型,这意味着您可以在main执行以下操作:

ArrayList<?> s = new ArrayList<String>();
f1.add(s);

ArrayList<?> i = new ArrayList<Integer>();
f1.add(i);

Now, let's assume that the compiler allows you to call go() with f1 as argument. 现在,让我们假设编译器允许您使用f1作为参数调用go() The option to infer T are: 推断T的选项是:

  1. T = Object , but ArrayList<ArrayList<Object>> is not ArrayList<ArrayList<?>> because ArrayList<Object> is not the same type as ArrayList<?> So that is not allowed option. T = Object ,但ArrayList<ArrayList<Object>>不是ArrayList<ArrayList<?>>因为ArrayList<Object>ArrayList<?>类型不同,所以不允许选项。

  2. T = ? , then we would be able to do: ,那么我们就能做到:

     public static <T> void go(ArrayList<ArrayList<T>> f) { ArrayList<T> alt1 = f.get(0); // ArrayList<String> T str = alt1.get(0); ArrayList<T> alt2 = f.get(1); // ArrayList<Integer> alt2.add(str); // We have added String to List<Integer> // ... type system broken } 

go() to work in both cases you have to do: go()在你必须做的两种情况下工作:

public static void go(ArrayList<? extends ArrayList<?>> f) {
}

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

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