"incompatible types" lambda/方法引用和泛型的编译器错误
"incompatible types" compiler error with lambda / method referencing and generics
我在处理一些旧代码时偶然发现了一个问题,用 lambda 表达式或方法引用替换了几个匿名 classes。这个问题有点难以用语言来解释,但我会尽力而为,我还在下面添加了一个简短的例子来说明我的问题。
我的示例包括...
一个函数式接口,GenericListener,它接受一个类型参数 V 并且只有一个方法 "genericCallback(V genericValue)".
A class,CallbackProducer,它接受一个类型的参数T。这个 class 还有一个方法可以添加一个 Integer 类型的 GenericListener。
A Main class 创建 CallbackProducers 并向其添加 GenericListeners。
当我从 Main 的构造函数 运行 CallbackProducer 的 addIntegerListener 方法时,每当我避免指定 CallbackProducer 的 T 的类型时,我都会得到 编译器错误:"incompatible types"。
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
上提供尖括号 <>
以避免使用原始类型,正如您在后续示例中所做的那样。
我在处理一些旧代码时偶然发现了一个问题,用 lambda 表达式或方法引用替换了几个匿名 classes。这个问题有点难以用语言来解释,但我会尽力而为,我还在下面添加了一个简短的例子来说明我的问题。
我的示例包括...
一个函数式接口,GenericListener,它接受一个类型参数 V 并且只有一个方法 "genericCallback(V genericValue)".
A class,CallbackProducer,它接受一个类型的参数T。这个 class 还有一个方法可以添加一个 Integer 类型的 GenericListener。
A Main class 创建 CallbackProducers 并向其添加 GenericListeners。
当我从 Main 的构造函数 运行 CallbackProducer 的 addIntegerListener 方法时,每当我避免指定 CallbackProducer 的 T 的类型时,我都会得到 编译器错误:"incompatible types"。
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
上提供尖括号 <>
以避免使用原始类型,正如您在后续示例中所做的那样。