Абстрактный класс Java, реализующий интерфейс с помощью обобщений - PullRequest
63 голосов
/ 29 августа 2010

Я пытаюсь определить абстрактный класс, реализующий Comparable. Когда я определяю класс со следующим определением:

public abstract class MyClass implements Comparable <MyClass>

Подклассы должны реализовать compareTo(MyClass object). Вместо этого я хочу, чтобы каждый подкласс реализовывал compareTo(SubClass object), принимая объект своего собственного типа. Когда я пытаюсь определить абстрактный класс с чем-то вроде:

public abstract class MyClass implements Comparable <? extends MyClass>

Он жалуется на то, что «в супертипе не может быть подстановочных знаков».

Есть ли решение?

Ответы [ 7 ]

43 голосов
/ 29 августа 2010

На мой взгляд, это слишком многословно, но работает:

public abstract class MyClass<T extends MyClass<T>> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}
18 голосов
/ 29 августа 2010

Помимо механических трудностей, с которыми вы сталкиваетесь при объявлении подписей, цель не имеет особого смысла.Вы пытаетесь установить ковариантную функцию сравнения, которая разрушает саму идею создания интерфейса, который могут адаптировать производные классы.

Если вы определяете некоторый подкласс SubClass, так что его экземпляры можно сравнивать только с другимиSubClass экземпляров, тогда как SubClass удовлетворяет договору, определенному MyClass?Напомним, что MyClass говорит, что его и любые производные от него типы можно сравнить с другими MyClass экземплярами.Вы пытаетесь сделать это неверным для SubClass, что означает, что SubClass не удовлетворяет контракту MyClass: вы не можете заменить SubClass на MyClass, потому что требования SubClass более строгие.

Эта проблема сосредоточена на ковариации и контравариантности, а также на том, как они позволяют сигнатурам функций изменяться при выводе типов.Вы можете ослабить требование к типу аргумента - принимая более широкий тип, чем требования сигнатуры супертипа - и вы можете усилить требование к типу возврата - обещая вернуть более узкий тип, чемподпись супертипа.Каждая из этих свобод все еще позволяет совершенную замену производного типа на супертип;вызывающая сторона не может определить разницу при использовании производного типа через интерфейс супертипа, но вызывающая сторона, использующая производный тип конкретно, может воспользоваться этими свободами.

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

3 голосов
/ 29 августа 2010

см. Собственный пример Java:

public abstract class Enum<E extends Enum<E>> implements Comparable<E>
    public final int compareTo(E o)

в комментарии Сэя: обычно аргумент верен.но дженерики усложняют отношения типов.Подкласс не может быть подтипом MyClass в решении Вилли ....

SubClassA является подтипом MyClass<SubClassA>, но не подтипом MyClass<SubClassB>

type MyClass<X> определяет контракт для compareTo(X), который должны соблюдаться всеми его подтипами.Там нет никаких проблем.

1 голос
/ 28 июля 2015

Нашел другое решение:

  1. Определить интерфейс для полей, которые составляют сопоставимый (например, ComparableFoo)
  2. Реализация интерфейса на родительском классе
  3. Реализация Comparable на родительском классе.
  4. Напишите вашу реализацию.

Решение должно выглядеть так:

public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> {
    public int compareTo(ComparableFoo o) {
    // your implementation
    }
}

Это решение подразумевает, что ComparableFoo может реализовать больше вещей - это, вероятно, не так, но тогда вы кодируете интерфейс, и универсальное выражение простое.

1 голос
/ 30 ноября 2012
public abstract class MyClass<T> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}
1 голос
/ 29 августа 2010

Я не уверен, что вам нужен захват:

Сначала добавьте CompareTo в абстрактный класс ...

public abstract class MyClass implements Comparable <MyClass> {

@Override
public int compareTo(MyClass c) {
...
}    
}

Затем добавьте реализации ...

public class MyClass1 extends MyClass {
...
}

public class MyClass2 extends MyClass {
...
}

При вызове сравнения будет вызываться метод типа super ...

MyClass1 c1 = new MyClass1();
MyClass2 c2 = new MyClass2();

c1.compareTo(c2);
0 голосов
/ 03 октября 2014

Я знаю, вы сказали, что хотите "сравнить (объект SubClass), принимая объект своего собственного типа", но я все же предлагаю объявить абстрактный класс следующим образом:

public abstract class MyClass implements Comparable <Object>

и выполните проверку экземпляра при переопределении CompareTo в MySubClass:

@Override
public int compareTo(Object o) {
    if (o instanceof MySubClass)) {
        ...
    }
    else throw new IllegalArgumentException(...)
}

аналогично «равно» или «клон»

...