Разница между «<T расширяет сопоставимый <T>>» и «<T расширяет сопоставимый <T>>>»? - PullRequest
2 голосов
/ 05 августа 2011

в выражении <T extends Comparable<T>> в подписи типа

public static <T extends Comparable<T>> foo(T x) { ... }

описание T рекурсивно зависит от Comparable<T>.

Если T расширяет Comparable<T>, а Comparable<T> расширяет Comparable<Comparable<T>>, не следует ли, что T расширяет Comparable<Comparable<T>>?

IOW, extends отношение транзитивно?

Если так, то почему не

public static <T extends Comparable<Comparable<T>>> int compare(T x, T y) {
    return x.compareTo(y);
}

эквивалентно

public static <T extends Comparable<T>> int compare(T x, T y) {
    return x.compareTo(y);
}

? Фактически, последнее определение компилируется, тогда как вышеприведенное определение не компилируется с ошибкой

comp.java:7: compareTo(java.lang.Comparable<T>) in 
java.lang.Comparable<java.lang.Comparable<T>> cannot be applied to (T)
            return x.compareTo(y);

Спасибо!

Ответы [ 5 ]

4 голосов
/ 05 августа 2011

Я думаю, вы совершили неверный логический прыжок.

T extends Comparable<T>

не не означает, что

Comparable<T> extends Comparable<Comparable<T>>

Краткая аналогия :

Предположим, что камнедробилка - это любой материал, который может раздробить камень , а камень просто способен разрушить сам себя. Так что скала - это камнедробилка. Означает ли это, что камень может разрушить любое вещество, которое может разрушить камень? Ясно, что нет: сталь также может быть дробилкой, в то время как камень может не разрушить сталь.


Длинная (на основе кода) аналогия :

Предположим, у меня есть интерфейс Additive<T>, который можно использовать для описания любого типа, с которым вы можете выполнить операцию add над T объектом и получить обратно T.

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

public static <T extends Additive<T>> add(T x, T y) { ... }

Поскольку x должен иметь тип, к которому я могу добавить T, этот метод может быть реализован следующим образом:

return x.add(y);

Имеет смысл? Ок, отлично; но теперь давайте сделаем предположение, которое вы сделали в своем вопросе: Additive<T> должно расширяться Additive<Additive<T>>; в таком случае я могу предположить, что могу добавить к x экземпляр любого типа, реализующего Additive<T>.

Вот где предположение нарушается. Я мог бы иметь тип, скажем ...

// A 32-bit integral number supporting addition
public class Int32 implements Additive<Int32> { ... }

Это имело бы смысл, верно? Я мог бы также иметь что-то вроде этого:

// A list of 32-bit integral numbers supporting appending
public class Int32List implements Additive<Int32> { ... }

Теперь, если T extends Additive<T> подразумевает, что Additive<T> extends Additive<Additive<T>>, тогда я смогу сделать это:

Additive<Int32> x = new Int32(5);
Additive<Int32> y = x.add(new Int32List());

Имеет ли это какой-то смысл?

2 голосов
/ 05 августа 2011

В

нет ничего особенного
<T extends Comparable<T>>

Рассмотрим

public class Foo implements Comparable<Foo>{
     int compareTo(Foo x);
}

Я даже не думаю, что мы можем сказать, что это определено рекурсивно, это просто самоссылка.

1 голос
/ 05 августа 2011

Фундаментальная проблема, которую следует иметь в виду, заключается в том, что обобщения Java ограничены единицей компиляции и временем компиляции. Это просто синтаксический сахар! (Сколько раз я об этом забываю и в конечном итоге рассуждаю именно так, как вы, , как если бы Generics Java фактически подразумевал систему универсальных типов во время выполнения .)

С Спецификация :

The scope of a class' type parameter is the entire declaration of the class 
including the type parameter section itself. 

Therefore, type parameters can appear as parts of their own bounds, or as 
bounds of other type parameters declared in the same section.

Нет рекурсии и не определены новые типы.

Для:

package so_6949760;

import java.util.HashMap;

public class SweetCompilingGenerics {

    @SuppressWarnings("serial")
    public static class Sweet<X> extends HashMap<X, Sweet<X>> {}

    public static void main(String[] args) {
        Sweet<String> sweetStr = new Sweet<String>();
        Sweet<Sweet<Integer>> whatever = new Sweet<SweetCompilingGenerics.Sweet<Integer>>();

        assert sweetStr.getClass().equals(whatever.getClass()) : "who made generics a runtime mechanism?";
        assert sweetStr.getClass() == whatever.getClass() : "who made generics a runtime mechanism?";

        System.out.format("Is %s a Sweet<String> ?\n", sweetStr.getClass().getCanonicalName());
        System.out.format("Is %s a Sweet<Sweet<Integer>> ?\n", whatever.getClass().getCanonicalName());
    }
}

Выводит сладкий секрет:

Is so_6949760.SweetCompilingGenerics.Sweet a Sweet<String> ? 
Is so_6949760.SweetCompilingGenerics.Sweet a Sweet<Sweet<Integer>> ?

Могу ли я с нетерпением ждать совета для такого же гика? Всегда читайте спецификацию .

[post accept edit]: только что нашел эту высокоинформативную статью IBM . Это в значительной степени отвечает на все вопросы ОП. (Рекомендуется).

1 голос
/ 05 августа 2011

Comparable<T> extends Comparable<Comparable<T>>

Возможно, я неправильно понимаю, какие отношения вы передаете, но это утверждение не соответствует действительности. Comparable<T> ничего не расширяет.

0 голосов
/ 05 августа 2011

Comparable<T> не расширяется Comparable<Comparable<T>> (только потому, что T расширяется Comparable<T>) так же, как List<String> не расширяется List<Object> (только потому, что String расширяется Object).

Пожалуйста, обратитесь к этому документу от Oracle, чтобы узнать больше о дженериках и наследовании.

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