Java泛型奇怪的行为

时间:2016-10-28 12:29:16

标签: java generics

用文字解释很难,但是Java Generics给了我一个意想不到的结果。我希望如果我说列表的类型为? extends Object,我可以在那里存储任何内容。因此,如果类型Wrapper<? extends Object>的列表,我可以在那里存储任何类型的包装器。等等。这对我来说很有意义。但是我们假设我们有:

private static class Wrapper<T> {
    public Wrapper(T t) { /**/ }
}

我想要的是:

private static final List<Wrapper<Wrapper<? extends Object>>> ls1 = new ArrayList<>();

请注意,这会给我一个错误:

public static <T> doit(T t) {
    Wrapper<Wrapper<T>> l1 = new Wrapper<>(new Wrapper<>(t));
    ls1.add(l1); // nok
    // add (Wrapper<Wrapper<? extends java.lang.Object>>) in List
    // cannot be applied to (Wrapper<Wrapper<T>>
}

但是如果我将Wrapper包装在辅助包装器(rs)中,那么:

private static class C<T> extends Wrapper<Wrapper<T>> {
    public C(T t) {
        super(new Wrapper<>(t));
    }
}

private static final List<C<? extends Object>> ls2 = new ArrayList<>();

public static <T> doit(T t) {
    ls2.add(new C<>(t)); // ok
}

请注意,它是一回事;这对我没有意义。

PS。在我的例子中,我没有制作包装器包装器,而是创建泛型类的ThreadLocal。

3 个答案:

答案 0 :(得分:2)

我认为

private static void AssignValueToProperty(ObjectAccessor accessor, object value, string propertyLambdaString)
{
    var index =  propertyLambdaString.IndexOf('.');

    if (index == -1)
    {
        accessor[propertyLambdaString] = value;
        // problem above: throws Exception if assigning value to Nullable<T>
    }
    else
    {
        var property = propertyLambdaString.Substring(0, index);
        accessor = ObjectAccessor.Create(accessor[property]);

        AssignValueToProperty(accessor, value, propertyLambdaString.Substring(index + 1));
    }
}

你可以这样做

public class A
{
  public double? SomeValue {get; set;}
}

...
var a = new A();
var accessor = ObjectAccessor.Create(a);
accessor["SomeValue"] = 100; // throws Exception, when assigning 100.0 it works???

答案 1 :(得分:0)

说实话,我必须更详细地研究它为什么不起作用。它最有可能与泛型类型擦除有关。 我有一个解决方法,我知道这个解决方案失去了l1对象的典型。如果你可以接受它。

Wrapper<Wrapper<? extends Object>> l1 = new Wrapper<>(new Wrapper<>(t));
ls1.add(l1);

答案 2 :(得分:0)

基本上你需要决定什么更重要,对于你想要放入包装器的内容,通过什么 out out 包装(你不能两者都安全)。决定之后,您需要在extendssuper之间进行选择。

如果只是想要存储指针,那么List<Wrapper<Wrapper>>就足够了,但之后你需要做一些演员。

当您使用 extends 时,? extends Foo表示结构返回的任何数据类型将是{{1}类型的子类型}(或Foo本身)。

例如,如果您有Foo,则可以传递void doIt1( List< ? extends Mammal> l ){}List<Human>List<Primate>List<Simian>

然而,当使用List<Mammal>时,你不知道什么是安全的。

考虑一下:

? extends

这显然不安全!我本可以void addElephant( List< ? extends Mammal> l ){ l.add( new Elephant() ); //wrong! } 传递List<Human>,现在我的l列表中有一个Human

另一方面,您有 Elephant ,其中super表示容器可以使用? super Foo

对于Foo,您可以传入void doIt2( List< ? extends Human> l ){}List<Human>List<Primate>List<Simian>List<Mammal>

List<Object>

这很好,但你不能保证你将从容器中取出什么。

void ( List< ? super Human> l  ){
    l.add( new Human() );
}

这是因为如果void ( List< ? super Human> l ){ Human h = l.get(0); //wrong! } 真的是l,它可能混合了List<Primate>Human,并且无法保证第一个元素将是Gorilla

为此,您将获得首字母缩写Human =&#34; [a] P roducer ** E xtends,[但a] C onsumer S upers&#34;。

如果你的容器正在将对象返回给某个方法(例如P.E.C.S),那么你就是&#34;生成&#34; (所以使用get),否则如果你接受一个容器的元素(例如extends),容器就会消耗掉#34;对象如此使用&#34; super&#34;。 ( Nb:要记住PECS的棘手部分是它来自容器的观点,而不是调用代码!)。

如果你想 生成和消费你,你需要有一个具体的类型列表add,你可以在其中添加List<Primate>并检索{{1 }}第

另见: