lambda /方法引用和泛型的“不兼容类型”编译器错误

时间:2015-08-05 18:29:37

标签: java generics lambda compiler-errors method-reference

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

我的例子包括......

  1. 功能接口 GenericListener ,它采用类型参数V 并具有单个方法“genericCallback(V genericValue)”。

    < / LI>
  2. 一个类 CallbackProducer ,它采用类型参数T 。该类还有一个添加类型为Integer的GenericListener的方法。

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

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

    方法addIntegerListener只使用GenericListener的V.据我所知,它不以任何方式使用CallbackProducer的T.

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

    此外,如果CallbackProducer没有使用泛型,并且我们完全删除了类型参数T,那么首先调用addIntegerListener将会编译。

    这种行为有原因吗?我误解了什么,或者这是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);
        }
    }
    

1 个答案:

答案 0 :(得分:1)

所有3个编译器错误都是由于您使用的是原始CallbackProducer。当您使用原始CallbackProducer时,所有类型参数都会进行类型擦除,这样任何T(例如您的)都没有任何上限,变为Object

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

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