Как работает рекурсивный generic?

264
12 марта 2017, 05:09

У меня есть код, по которому я хочу разобраться, как работает рекурсивные generics. Смысл задачи в том, что-бы метод compareTo, принимал для сравнения, только объекты того типа, на котором он вызывается. То есть от класса Product наследуется Milk, и Phone, и объект Phone не должен принимать для сравнения в compareTo, объект Milk, только Phone. И есть место, которое я не как не могу понять. Это место Product<T extends Product<T>> автор кода утверждает, что для правильной работы, необходимо параметризировать Product<T>. Но код работает точно так же, если написать Product<T extends Product>. Объясните пожалуйста, это я что-то не понимаю, или автор перемудрил. И если первое, то почему так?

class Product<T extends Product<T>> implements Comparable<T> { 
    private int price;
    Product(int price) {
        this.price = price;
    }
    public int getPrice() {
        return price;
    }
    @Override
    public int compareTo(T o) {
        return o.getPrice() - this.price;
    }
}
class Milk extends Product<Milk> {
    Milk(int price) {
        super(price);
    }
}
class Phone extends Product<Phone> {
    Phone(int price) {
        super(price);
    }
}
Answer 1

Использование T extends Product<T> вместо T extends Product не препятствует созданию классов, у которых в качестве T участвует другой тип или T вообще не указан:

class Phone extends Product<Milk> { ... }
class Chair extends Product { ... }

Однако в случае использования T extends Product<T> не получится создать такой класс:

class CustomChair extends Product<Chair>

потому что это приведёт к ошибке компиляции:

error: type argument Chair is not within bounds of type-variable T
class CustomChair extends Product
where T is a type-variable:
T extends Product declared in class Product

Ошибка компиляции будет даже если класс Chair задан так:

class Chair extends Product<Milk> { ... }

Так как в обоих случаях класс Chair не подходит в качестве T из-за требования T extends Product<T>. Вот Chair extends Product<Chair> подошло бы.

Можно сказать, что T extends Product<T> задаёт более жесткие условия для T, чем T extends Product. Где-то это нужно, где-то - нет.

Например, если бы в классе Product были такие поля:

private T friend, friendOfFriend;

и такие методы:

public void setFriend(T t)
{
    friend = t;
    friendOfFriend = t.getFriend();
}
public T getFriend() { return friend; }
public T getFriendOfFriend() { return friendOfFriend; }

То в случае использования T extends Product пришлось бы использовать

friendOfFriend = (T)t.getFriend();

для приведения Product к T, что чревато ClassCastException.
Например, при всё тех же class Chair extends Product и class CustomChair extends Product<Chair>:

Milk milk = new Milk(1);
Chair chair = new Chair(10);
chair.setFriend(milk);
CustomChair customChair = new CustomChair(20);
customChair.setFriend(chair);
Chair c = customChair.getFriendOfFriend();

В реальных условиях, надеюсь, никто не пытается подружить стул с молоком, но всё же.

С T extends Product<T> молоко получится подружить только с молоком:

Milk milk1 = new Milk(1);
Milk milk2 = new Milk(2);
Milk milk3 = new Milk(3);
milk2.setFriend(milk3);
milk1.setFriend(milk2);
Milk m = milk1.getFriendOfFriend();
System.out.println(m.getPrice());
READ ALSO
Определить, находится ли курсор в JTextField

Определить, находится ли курсор в JTextField

В общем, проблема такая: мне нужно проверить находится ли _курсор в объекте JTextField

275
Как убрать лишние символы со строки?

Как убрать лишние символы со строки?

Есть строка: String a = "[49,5454545,434324]";

439
Read integers from file and find maximum number , and all indexes with this maximum number

Read integers from file and find maximum number , and all indexes with this maximum number

Необходимо прочитать все целые числа (integer) из файла и найти максимальное число, так же необходимо найти все индексы этого числа

311
Не могу сделать hover

Не могу сделать hover

Нужно сделать такой hover:

252