Я наткнулся на проблему при работе с каким-то старым кодом, заменив несколько анонимных классов либо лямбда-выражениями, либо ссылками на методы. Проблема немного сложна для объяснения словами, но я сделаю все возможное, и я также добавил короткий пример, иллюстрирующий мою проблему, насколько это возможно из моих возможностей ниже.Ошибка компилятора «несовместимых типов» с привязкой лямбда/метода и генериками
Мой пример состоит из ...
Функциональный интерфейс, GenericListener, который принимает параметр Vтипа и имеет единственный метод "genericCallback (V genericValue)".
Класс, CallbackProducer, который принимает параметр Tтипа. Этот класс также имеет метод добавления GenericListener с типом Integer.
A Основной класс, который создает CallbackProducers и добавляет к ним GenericListeners.
Когда я запускаю метод addIntegerListener CallbackProducer от конструктора Main, я получаю ошибку компилятор: «несовместимые типы» всякий раз, когда я избегать указания типа T. CallbackProducer в
Метод addIntegerListener использует только V GenericListener в Насколько я знаю, он не использует CallbackProducer's T никоим образом.
Я поместил несколько вызовов addIntegerListener + комментариев в конструкторе Main, 3 из которых вызывают ошибки компилятора. Но, насколько я вижу (и, по мнению IntelliJ), все они должны быть законными. Если вы закомментируете 3 первых вызова addIntegerListener, приложение будет компилироваться и работать просто отлично.
Кроме того, если CallbackProducer не использовал дженерики, и мы полностью удалили параметр типа T, будут скомпилированы 3 первых вызова 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);
}
}
Спасибо за ответ. То, что вы говорите, может для всех, кого я знаю очень хорошо, является причиной, по которой компилятор жалуется, но CallbackProducer's никогда не используется. «addIntegerListener» уже принимает в качестве аргумента GenericListener . Кроме того, следующие строки компилируются и выполняются просто отлично: 'CallbackProducer производитель2 = new CallbackProducer <>(); производитель2.addIntegerListener (this :: integerReceived); ' Теперь я говорю, что T - это Double, когда я работаю с целыми слушателями. Он по-прежнему работает, поскольку T не имеет отношения к слушателю. –
Serenic
Это не имеет никакого отношения к тому, что такое аргумент типа 'CallProducer'. Он имеет все, что связано с тем, что вы использовали raw 'CallProducer'. – rgettman
Просто, чтобы убедиться, что я понимаю: вы говорите, что если я использую необработанный CallbackProducer (или любой другой класс, который принимает параметр типа), все другие аргументы типа этого производителя будут стираться, даже если они полностью не связаны с «T» CallbackProducer? (как в моем случае) Я не сомневаюсь, что вы правы, но из любопытства, есть ли причина для такого поведения? Я не понимаю, почему это должно быть проблемой. Типы гарантируют соответствие. Знаете ли вы, что это слабость в компиляторе, который может быть рассмотрен в будущем, или если это по дизайну? – Serenic