Иногда у нас есть несколько классов, которые имеют некоторые методы с одинаковой сигнатурой, но не соответствуют объявленному интерфейсу Java. Например, и JTextField
, и JButton
(среди нескольких других в javax.swing.*
) имеют метод
public void addActionListener(ActionListener l)
Теперь предположим, что я хочу что-то сделать с объектами, у которых есть этот метод; тогда я хотел бы иметь интерфейс (или, возможно, определить его сам), например
public interface CanAddActionListener {
public void addActionListener(ActionListener l);
}
чтобы я мог написать:
public void myMethod(CanAddActionListener aaa, ActionListener li) {
aaa.addActionListener(li);
....
Но, к сожалению, я не могу:
JButton button;
ActionListener li;
...
this.myMethod((CanAddActionListener)button,li);
Это действие было бы незаконным. Компилятор знает, что JButton
не CanAddActionListener
, потому что класс не объявлен для реализации этого интерфейса ... однако он "фактически" реализует его em >.
Иногда это доставляет неудобства - и сама Java изменила несколько основных классов, чтобы реализовать новый интерфейс, состоящий из старых методов (например, String implements CharSequence
).
У меня вопрос: почему это так? Я понимаю полезность объявления того, что класс реализует интерфейс. Но в любом случае, глядя на мой пример, почему компилятор не может сделать вывод, что класс JButton
"удовлетворяет" объявлению интерфейса (заглядывая в него), и принять приведение? Это вопрос эффективности компилятора или есть более фундаментальные проблемы?
Краткое изложение ответов: это случай, когда Java могла сделать скидку на некоторую «структурную типизацию» (своего рода утиную типизацию, но проверяемую во время компиляции). Это не так. Помимо некоторых (непонятных для меня) трудностей с производительностью и реализацией, здесь есть гораздо более фундаментальная концепция: в Java объявление интерфейса (и вообще всего) не должно быть просто структурным strong> (чтобы иметь методы с этими сигнатурами), но семантический: предполагается, что методы реализуют какое-то конкретное поведение / намерение. Таким образом, класс, который структурно удовлетворяет некоторому интерфейсу (т. Е. Имеет методы с необходимыми сигнатурами), не обязательно удовлетворяет ему семантически (крайний пример: вспомните маркер интерфейсы », у которых даже нет методов!). Следовательно, Java может утверждать, что класс реализует интерфейс, потому что (и только потому, что) он был явно объявлен. У других языков (Go, Scala) другая философия.