"класс А"работает не так, как в классе B?(Да) - PullRequest
0 голосов
/ 23 февраля 2019

Хотя определение, включающее интерфейс Comparable

class B <T extends Comparable<T>>

, хорошо известно и может быть реализовано, одно и то же использование не работает таким же образом при расширении класса вместо интерфейса.Вот пример

class G <T extends Box<T>> {
    T val;
    G(T val) {
        this.val = val;
    }
}

Вышеупомянутый класс компилируется, но я не могу найти способ создания экземпляра G. Кажется, требуется бесконечная вложенность Box.

G<Box<Box<..>>>

Вотмое определение класса Box

class Box <T> {
    T val;
    Box(T val) {
        this.val = val;
    }
}

Чтобы помочь в обсуждении, вот несколько подобных примеров.Я проверил весь свой код.

Значение следующего класса мне ясно

class D <T extends Box<String>> {
    T val;
    D(T val) {
        this.val = val;
    }
}

мы можем создать его экземпляр, используя

    D<Box<String>> d = new D<>( new Box<String> ("hello") );

Мы можем обобщить это такчто в Ящике может содержаться что-нибудь

class F <S, T extends Box<S>> {
    T val;
    F(T val) {
        this.val = val;
    }
}

, которое мы можем создать, используя

    F<String,Box<String>> f = new F<>( new Box<String> ("hello") );

. Вернемся к первоначальному вопросу: что означает следующее и как / можно его создать?

class G <T extends Box<T>> {
    T val;
    G(T val) {
        this.val = val;
    }
}

Этот класс G был моей первой попыткой обобщить приведенный выше класс D так, чтобы Box мог содержать любой тип, а не только String.Позже я придумал класс F в качестве решения моей проблемы, но мне интересно, что означает G и почему он отличается от T extends SomeInterface<T>.

Ответы [ 3 ]

0 голосов
/ 23 февраля 2019

Если вас беспокоит ограничение типа T extends Box<T> означает, что только типы, которые можно указать в качестве параметра типа, должны расширяться Box.

Этот метод используется для указания дополнительных операций, которые вы можете выполнять сваш тип (например, общеизвестный Comparable<T>), так что использование класса вместо интерфейса не очень распространено.

Позвольте привести пример.Скажем, у нас есть следующие операции

interface Op1<T>{
    void doOp1(t: T): T
}

interface Op2<T> {
   void doOp2(t: T): T
}

class MyClass implements Op1<MyClass>, Op2<MyClass>{
    //By implementing Op1 and Op2 you
    //specify that operations doOp1 and doOp2
    // can be applied to variable of typr MyClass
}

И теперь вы хотите реализовать свой универсальный контейнер, который принимает элементы типов, к которым Op1 и Op2 могут быть применены

class MyContainer<T extends Op1<T> & Op2<T>>{
   //you can apply doOp1 and doOp2 to any variable of the type T
}

MyContainer<MyClass> t = //... Ok
MyContainer<Integer> t = //... Not ok

В функциональном программировании похожая (но не совсем та же) вещь называется typeclasses

0 голосов
/ 24 февраля 2019

ОПИСАНИЕ ОТВЕТА

На основе ответов @Ruslan и @Some Name и комментариев на вопрос, здесь приводится краткий обзор различий между

class G <T extends SomeClass<T>>
class B <T extends SomeInterface<T>>  
class Enum<E extends Enum<E>>   

. Я обнаружил, что написаниепомогает проверенные примеры.

1.class G <T extends SomeClass<T>>

Класс G требует, чтобы тип T был определен как

class T extends SomeClass<T> {}

Вот пример класса, подобного T

class RussianBox extends Box<RussianBox> {
    RussianBox(RussianBox box) {
        super(box);
    }
}

где класс Box определяется как

class Box <T> {
    T value; 
    Box(T value) {
        this.value = value;
    }
    public String toString() {
        return this.getClass().getSimpleName()+" containing {"+this.value+"}";
    }
}

Вот пример класса, подобного G

class RussianBoxContainer <T extends Box<T>>   {
    T value; 
    RussianBoxContainer(T value) {
        this.value = value;
    }
    public String toString() {
        return this.getClass().getSimpleName()+" containing {"+this.value+"}";
    }
}

В следующем коде создаются эти три класса

    Box<String> box = new Box<>("Gold");
    out.println("box = " + box );

    RussianBox russianBox = new RussianBox( new RussianBox(null) );
    out.println("russianBox = " + russianBox );

    RussianBoxContainer<RussianBox> containerForARussianBox = new RussianBoxContainer<>(russianBox);
    out.println("containerForARussianBox = " + containerForARussianBox );

и выдает следующий вывод

box = Box containing {Gold}
russianBox = RussianBox containing {RussianBox containing {null}}
containerForARussianBox = RussianBoxContainer containing {RussianBox containing {RussianBox containing {null}}}

2.class B <T extends SomeInterface<T>>

Класс B требует, чтобы тип T был определен как

class T extends SomeClass implements SomeInterface<T> {}

Класс java.lang.Integer является примером класса, подобного T

class Integer extends Number implements Comparable<Integer>

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

3.class Enum<E extends Enum<E>>

Теперь создайте класс , например Enum.Вот пример

class RussianBoxContainer2 <T extends RussianBoxContainer2<T>>   {
    RussianBoxContainer2<T> value; 
    RussianBoxContainer2(RussianBoxContainer2<T> box) {
        this.value = box;
    }
    public String toString() {
        return this.getClass().getSimpleName()+" containing {"+this.value+"}";
    }
}

Как и Enum, нам нужно создать подкласс RussianBoxContainer2, прежде чем мы сможем использовать RussianBoxContainer2.Вот пример подкласса

class RussianBox2 extends RussianBoxContainer2<RussianBox2> {
    RussianBox2(RussianBox2 box) {
        super(box);
    }
}

В следующем коде создаются экземпляры этих двух классов

RussianBox2 russianBox2 = new RussianBox2(new RussianBox2(null));
out.println("russianBox2 = " + russianBox2 );

RussianBoxContainer2<RussianBox2> containerForARussianBox2 = new RussianBoxContainer2<>(russianBox2);
out.println("containerForARussianBox2 = " + containerForARussianBox2 );

и выдается следующий вывод

russianBox2 = RussianBox2 containing {RussianBox2 containing {null}}
containerForARussianBox2 = RussianBoxContainer2 containing {RussianBox2 containing {RussianBox2 containing {null}}}
0 голосов
/ 23 февраля 2019

Если у класса Box нет конструктора с T, вы можете создать класс, расширяющий Box.Например:

class NewBox extends Box<NewBox> {
    ...
}

Затем вы можете создать экземпляр G, например:

G<NewBox> g = new G<>(new NewBox());

Но в вашем случае Box имеет конструктор Box(T val){...}, тогда NewBox требуется соответствующий конструктор super like:

class NewBox extends Box<NewBox> {
    NewBox(NewBox val) {
        super(val);
    }
}

Чтобы создать его, вы должны получить нулевое значение, иначе это приведет к бесконечной вложенности:

G<NewBox> g = new G<>(new NewBox(new NewBox(new NewBox(null))));

Обновление: , чтобы ответить на вашИсходный вопрос: G <T extends Box<T>> означает, что T должно быть типа Box или любого потомка Box.Как вы правильно заметили, это приведет к бесконечной вложенности.Но вы все еще можете создать этот экземпляр без создания дополнительного класса (как указано выше с NewBox), используя Wildcard и null в качестве параметра для конструктора класса G, например:

G<?> tg = new G<>(null);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...