【问题标题】:Generics <? super> wildcard not working in java 1.8 with method reference or lambda泛型 <? super> 通配符在带有方法引用或 lambda 的 java 1.8 中不起作用
【发布时间】:2019-09-19 01:59:30
【问题描述】:
    package com.test.lambda;

    import java.util.function.Supplier;

    class Document {
        void printAuthor() {
            System.out.println("Document-Author");
        }
    }

    class RFP extends Document {
        @Override
        void printAuthor() {
            System.out.println("RFP-Author");
        }
    }

    public class TestLambda1 {
        public static void function21() {
            Supplier<Document> s1 = Document::new; // working
            Supplier<Document> s2 = RFP::new; // (1)

            Supplier<? extends Document> s3 = Document::new; // working
            Supplier<? extends Document> s4 = RFP::new; // working

            Supplier<? super Document> s5 = Document::new; // working
            Supplier<? super Document> s6 = RFP::new; // (2)

            Supplier<? super RFP> s7 = Document::new; // (3)
            Supplier<? super RFP> s8 = RFP::new; // working

        }

        public static void main(String[] args) throws Exception {
            function21();
        }

    }

(1) (2) & (3) 中的问题是,它应该像 java 1.7 一样工作 (1) :它应该给出错误,因为应该只接受文档类型。 (2) :它应该给出错误,因为应该只接受超类型的 Document。 (3) : 它应该作为 RFP 的 super 可以保存 Document 对象。 Difference between <? super T> and <? extends T> in Java

【问题讨论】:

  • 链接答案的哪一部分不能关联?因为这些答案已经很详细了。
  • 显然,类型推断非常复杂,但我希望 (1) 和 (2) 能够工作。毕竟,RFP::new 确实会产生一个 Document 对象。我同意(3)应该有效。您实际上可以转换它,(Supplier&lt;Document&gt;) Document::new,它会编译而不会出现任何错误或警告。
  • @VGR 在这种情况下,它并不复杂。这是由于语言设计者为了避免(甚至更高的)复杂性而故意削减的。

标签: java generics lambda java-8 method-reference


【解决方案1】:

在 Java 1.7 和 Java 1.7 中坚持“它应该像 java 1.7 一样工作”是很奇怪的。 Java 7 根本没有方法引用。

当你写这样的语句时

Supplier<Document> s2 = RFP::new;

你必须区分变量的类型、函数的类型和实际的实现。你可以很容易地写出等价的

Supplier<Document> s2 = new Supplier<Document>() {
    public Document get() {
        return new RFP();
    }
}; // (1)

由于协变返回类型,你也可以写

Supplier<Document> s2 = new Supplier<Document>() {
    public RFP get() {
        return new RFP();
    }
}; // (1)

所以变量的类型匹配函数的类型,而实现返回一个更具体类型的实例。

当你写作时

Supplier<? extends Document> s3 = Document::new; // working
Supplier<? extends Document> s4 = RFP::new; // working

Supplier<? super Document> s5 = Document::new; // working
Supplier<? super Document> s6 = RFP::new; // (2)

函数的类型会有所不同。你不能写new Supplier&lt;? extends Document&gt;() { … }new Supplier&lt;? super Document&gt;() { … },所以编译器会推断出一个可以被实例化的类型,它只是没有? extends? super 的目标类型。所以相当于

Supplier<? extends Document> s3 = (Supplier<Document>)Document::new; // working
Supplier<? extends Document> s4 = (Supplier<Document>)RFP::new; // working

Supplier<? super Document> s5 = (Supplier<Document>)Document::new; // working
Supplier<? super Document> s6 = (Supplier<Document>)RFP::new; // (2) just like (1)

这是有效的实例化(如在第一个块中),然后是合法的分配。

问题

Supplier<? super RFP> s7 = Document::new; // (3)

完全符合上述类型推断逻辑。类型推断将使用没有? super 的类型,(Supplier&lt;RFP&gt;)Document::new 无效。所以在这里,我们必须提供一个显式类型以使其有效:

Supplier<? super RFP> s7 = (Supplier<Document>)Document::new; // (3)

它遵循与第二块相同的模式。
你不能实现Supplier&lt;? super RFP&gt;。您可以实现像Supplier&lt;Document&gt; 这样的供应商,它可以分配给Supplier&lt;? super RFP&gt;。当目标类型有通配符时,去掉通配符的策略通常会有所帮助,但有时不会。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多