Java Generics超级/扩展 - 不同的行为(用于OCP java 8学习目的)

时间:2018-05-08 23:46:29

标签: java generics lambda extends super

假设我有以下结构:

const recentOrders = Object.keys(data)
    .filter(function (key) { 
        return key.startsWith('L_') 
    })
    .map(function (key) { 
        const parts = key.match(/^L_([A-Z]+)(\d+)$/); 
        return { 
            key: parts[1].toLowerCase(),
            value: data[key], 
            index: parseInt(parts[2]) 
        };  
    })
    .reduce(function (recentOrders, item) { 
        if (recentOrders[item.index] == null) { 
            recentOrders[item.index] = {} 
        } 
        recentOrders[item.index][item.key] = item.value; 
        return recentOrders ;
    }, []);

console.log(recentOrders);

现在我正在编写一个主要方法来使用它。 我对以下概念很满意:('对象'从超级方法的get方法返回类型)

java.lang.Object
class A extends Object { public boolean mA() {return true;} }
class B extends A { public boolean mB() {return false;} }

因为我可以使用

来调用它
public static void m1(List<? super A> l ) {
    l.get(0).toString();
}

我对此也很好:(&#39; A&#39;类型从get方法返回以进行扩展)

m1(new ArrayList<A>());
m1(new ArrayList<Object>());

因为我可以使用

来调用它
public static void m2(List<? extends A> l ) {
    l.get(0).mA();
}

最后我的问题:为什么下面的定义是&#39; super&#39;接收类型&#39; A&#39;作为参数,而不是对象?

m2(new ArrayList<A>());
m2(new ArrayList<B>());

我认为它会变成类似的东西(我知道下面的代码无效......我只是觉得它会与之类似)。

Predicate<? extends A> p1 = a -> a.mA(); // ok
Predicate<? super A> p2 = a -> a.mA(); // why is it of type 'A' too??

我还检查了反编译类,但我没有得到任何新的线索。谁能告诉我引擎盖下发生了什么?我已经阅读了PECS讨论,但它对这个具体问题没有帮助。

提前谢谢! ;)

=====收到答案后用额外代码编辑=====

PredicateImpl<T super A> {
    public boolean test(T t);
}

有趣的替代语法(虽然我认为Predicate&lt;?extends Anything&gt;是非常没用的,因为它只接受&#39; null&#39;在测试方法)。无论如何,我发现这样的练习,无论如何我都学会了。

Predicate<? super A> pSuper = a -> a.mA();
Predicate<? extends A> pExtends = a -> a.mA();      // ?? A ??

Predicate<Object> pObject = o -> o.equals(null);
Predicate<A> pA = a -> a.mA();
Predicate<B> pB = b -> b.mB();

pSuper = pObject;
pSuper = pA;
pSuper = pB;        // compiler error

pExtends = pObject; // compiler error
pExtends = pA;
pExtends = pB;

1 个答案:

答案 0 :(得分:3)

JLS, Section 9.9,讨论了lambda表达式的类型推断。其中一节讨论了存在通配符和边界时的类型推断。

  

当通用功能接口通过通配符参数化时,有许多不同的实例可以满足通配符并生成不同的函数类型。例如,每个Predicate<Integer>(函数类型Integer - &gt; boolean),Predicate<Number>(函数类型Number - &gt; boolean) ,Predicate<Object>(函数类型Object - &gt; boolean)是Predicate<? super Integer>。有时,可以从上下文中了解,例如lambda表达式的参数类型,函数类型是什么(第15.27.3节)。其他时候,有必要选一个;在这些情况下,使用界限

(大胆强调我的)

此处,目标类型为Predicate<? super A>,lambda参数a可能有多种类型:ObjectA。目标类型绑定为? super A,因此绑定的A用作类型。

这对于任何通配符绑定(上限或下限)都是最有意义的。使用上限,绑定类型非常有意义。类型为A或更低,但它可能只是A,因此A方法应该可用。 Predicate<A>可分配给Predicate<? extends A>

使用下限,类型为A或更高。但为什么不选择Object?因为A工作正常。

Predicate<A>可以分配给Predicate<? super A>Predicate<Object>也可以分配给Predicate<? super A>,但这会造成不必要的限制。 lambda表达式仅限于调用Object方法,因为它可以轻松调用A方法(方法mA)。在这里选择最具限制性的类型是有意义的,这样程序员可以调用大多数方法。此外,根据边界限制最多的功能接口类型必须是可分配的。 Predicate<A>可分配给Predicate<? super A>

您甚至可以将lambda表达式a -> a.mA()分配给Predicate<? extends B>Predicate<? super B>,甚至Predicate<B>。推断类型为B,该类型肯定具有方法mA,从A继承。

如果您尝试将lambda表达式a -> a.mA()分配给Predicate<Object>Predicate<?>,则Object必须是推断类型。在这里,您将得到mA未定义Object的编译器错误。