Почему универсальный класс Java может реализовать метод универсального интерфейса с параметром Object? - PullRequest
0 голосов
/ 22 октября 2018

Вот код Java:

public interface myInterface<T> {
    void doSomething(T yes);
}

private static class myInterfaceImpl<T> implements myInterface<T>{
    @Override
    public void doSomething(Object yes) {

    }
}

Эта вещь компилируется, хотя я думаю, что класс не переопределяет метод из интерфейса.По выводу типа я бы предположил, что параметр типа T всегда равен Object, так как класс реализует метод из интерфейса, где он явно указывает Object как параметр.Версия с (T yes) в переопределенном методе также работает, но это очевидно для меня.Подскажите, пожалуйста, почему так обстоит дело с примером, который я представил?

Спасибо.

Ответы [ 3 ]

0 голосов
/ 22 октября 2018

Итак, вы наткнулись на Ковариацию и Контравариантность.Допустим, у вас есть:

IMyInterface<T> {
  void foo(T o);
}

и вы реализуете

class A implements IMyInterface<Integer> {
  void foo(Integer o) { ... }
}

, вы также можете написать:

class A implements IMyInterface<Integer> {
  void foo(Number o) { ... }
}

class A implements IMyInterface<Integer> {
  void foo(Object o) { ... }
}

, начиная с параметра тип T гарантированно будет целым числом -> o также гарантированно будет числом или объектом -> потому что целое число расширяет число расширяет объект

, так что вы ослабляете тип!Это то, что делает ваш код.

0 голосов
/ 22 октября 2018

Здесь стирание типов, механизм, используемый Java для обратной совместимости при представлении обобщений в JDK 1.5, делает вещи немного нелогичными.

Обычно можно ожидать, что сигнатура переопределяющего метода должнасоответствовать точно.Это не совсем так.Хотя точное совпадение с подписью, безусловно, переопределит метод, стирание подписи также переопределит метод. JLS, раздел 8.4.8.1 , сообщает:

Метод экземпляра m C , объявленный или унаследованный классом C, переопределяет из C другой метод m A объявлено в классе A, если все следующее верно:

- snip

Подпись m C является подписью (§8.4.2) подписи m A .

и раздела 8.4.2 состояний:

Подпись метода m 1 является подписью подписи метода m 2 , если либо:

  • m 2 имеет ту же подпись, что и m 1 или

  • подпись m 1 - это то же самое, что и стирание (§4.6) подписи m 2 .

Поскольку стирание T равно Object, разрешено переопределять (здесь реализовывать) метод методом, который принимает Object.Если вы установите верхнюю границу для T в интерфейсе, вы больше не сможете переопределить метод с помощью Object;это должна быть та верхняя граница, которая является стиранием.

// Example of erasure with upper bound
interface myInterface<T extends Number> {
    void doSomething(T yes);
}

class myInterfaceImpl<T extends Number> implements myInterface<T>{
    @Override
    public void doSomething(Number yes) {

    }
}

Обратите внимание, что здесь не допускается контрастность параметров.С верхней границей вы не можете переопределить doSomething с помощью метода, принимающего супертип типа параметра, например Object.

// A compiler error occurs here when the erasure is Number.
@Override
public void doSomething(Object yes) {

}
0 голосов
/ 22 октября 2018

Я, вероятно, нашел ответ :

Во время процесса стирания типа, компилятор Java стирает все параметры типа и заменяет каждый его первой границей, если параметр типа ограничен, илиОбъект, если параметр типа неограничен.

Рассмотрим следующий универсальный класс, представляющий узел в односвязном списке:

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

Поскольку параметр типа T неограничен, компилятор Java заменяетэто с Object:

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

В следующем примере универсальный класс Node использует параметр ограниченного типа:

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

Компилятор Java заменяет параметр ограниченного типа T первой границейкласс, сопоставимый:

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

Спасибо @Oleksandr за это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...