java - "incompatible types"lambda/方法引用和泛型编译器错误

标签 java generics lambda compiler-errors method-reference

我在处理一些旧代码时偶然发现了一个问题,用 lambda 表达式或方法引用替换了几个匿名类。这个问题有点难以用语言来解释,但我会尽力而为,我还在下面添加了一个简短的例子来说明我的问题。

我的示例包括...

  1. 一个功能接口(interface),GenericListener,它接受一个类型参数 V 并且有一个方法“genericCallback(V genericValue)”。

  2. 一个类,CallbackProducer,它带有一个类型参数 T。这个类还有一个方法来添加一个 Integer 类型的 GenericListener。

  3. Main 类,它创建 CallbackProducers 并向其添加 GenericListeners。

当我从 Main 的构造函数运行 CallbackProducer 的 addIntegerListener 方法时,每当我避免指定 CallbackProducer 的 T 的类型时,我都会收到编译器错误:“不兼容的类型”

addIntegerListener 方法仅使用 GenericListener 的 V。据我所知,它没有以任何方式使用 CallbackProducer 的 T。

我在 Main 的构造函数中多次调用 addIntegerListener + 注释,其中 3 次导致编译器错误。但据我所知(根据 IntelliJ 的说法)所有这些都应该是合法的。如果您注释掉对 addIntegerListener 的 3 个第一次调用,应用程序将编译并运行得很好。

此外,如果 CallbackProducer 不使用泛型,并且我们完全删除了类型参数 T,则对 addIntegerListener 的前 3 次调用将编译通过。

这种行为有原因吗?我是不是误会了什么,或者这是 java 编译器中的弱点或错误? (我目前使用的是java 1.8_51)

提前感谢您的澄清!

import javax.swing.*;

public class Main {

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::new);
    }

    public Main() {

        // Compiler error, type of CallbackProducer's "T" not specified
        CallbackProducer producer1 = new CallbackProducer();
        producer1.addIntegerListener(this::integerReceived);

        // Compiler error, no diamond brackets for CallbackProducer
        new CallbackProducer().addIntegerListener(this::integerReceived);

        // Also compiler error for lambdas with no diamond brackets on CallbackProducer
        new CallbackProducer().addIntegerListener(intValue -> integerReceived(intValue));

        // Works because a (any) type for CallbackProducer's "T" is specified
        CallbackProducer<Object> producer2 = new CallbackProducer<>();
        producer2.addIntegerListener(this::integerReceived);

        // Works because of the diamond brackets
        new CallbackProducer<>().addIntegerListener(this::integerReceived);

        // Lambda also works with diamond brackets
        new CallbackProducer<>().addIntegerListener(intValue -> integerReceived(intValue));

        // This variant also works without specifying CallbackProducer's "T"
        // ... but it is a workaround I'd prefer to avoid if possible :-P
        GenericListener<Integer> integerListener = this::integerReceived;
        new CallbackProducer().addIntegerListener(integerListener);
    }

    private void integerReceived(Integer intValue) {
        System.out.println("Integer callback received: " + intValue);
    }

    // A callback producer taking generic listeners
    // Has a type parameter "T" which is completely unrelated to
    // GenericListener's "V" and not used for anything in this
    // example really, except help provoking the compiler error
    public class CallbackProducer<T> {
        // Adds a listener which specifically takes an Integer type as argument
        public void addIntegerListener(GenericListener<Integer> integerListener) {
            // Just a dummy callback to receive some output
            integerListener.genericCallback(100);
        }
    }

    // A simple, generic listener interface that can take a value of any type
    // Has a type parameter "V" which is used to specify the value type of the callback
    // "V" is completely unrelated to CallbackProducer's "T"
    @FunctionalInterface
    public interface GenericListener<V> {
        void genericCallback(V genericValue);
    }
}

这是一个精简版,没有所有注释困惑,只有两次调用“addIntegerListener”,其中一次会导致编译器错误。

import javax.swing.*;

public class Main {

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::new);
    }

    public Main() {

        CallbackProducer producer1 = new CallbackProducer();
        producer1.addIntegerListener(this::integerReceived);    // Compiler error

        CallbackProducer<Object> producer2 = new CallbackProducer<>();
        producer2.addIntegerListener(this::integerReceived);    // Compiles OK      
    }

    private void integerReceived(Integer intValue) {
        System.out.println("Integer callback received: " + intValue);
    }

    public class CallbackProducer<T> {
        public void addIntegerListener(GenericListener<Integer> integerListener) {
            integerListener.genericCallback(100);
        }
    }

    @FunctionalInterface
    public interface GenericListener<V> {
        void genericCallback(V genericValue);
    }
}

最佳答案

所有 3 个编译器错误都是由于您使用的是原始 CallbackProducer。 .当您使用原始 CallbackProducer ,所有类型参数都经过类型删除,这样任何 T ,比如你的,没有任何上限,变成 Object .

因此,addIntegerListener方法需要原始 GenericListener作为参数,integerReceived不再适合。 integerReceived方法采用 Integer ,不是 Object , 作为原始 GenericListener会供应。

您必须提供尖括号 <>CallbackProducer避免使用原始类型,正如您在后续示例中所做的那样。

关于java - "incompatible types"lambda/方法引用和泛型编译器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31840158/

相关文章:

C# - 将 List<T>.Find() 与自定义对象一起使用

C# 泛型运算符

java - 可选列表和以下映射

python - 使用 lambda 提高迭代数据帧时的代码效率

java - ADF Faces 转义按钮不会触发 SelectionListener af :table 的 SelectionEvent

java - Derby 数据库 : how to find string in Blob field

java - spring security 已经给了token 是不是还要写一个代码把token 存到redis 中?

c# - 将openCV的vector<Point2f>转换为C#的Collections::Generic::List<Windows::Point>

java - 无法更改 Swing 中的 Activity 选项卡, "Non static method cannot be referenced"

java - 如何在 Java 中使用 lambda 函数对每列和每行的列表列表求和?