что может означать это общее объявление класса?

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

Ниже приведен общий класс, который появился в моем вопросе на собеседовании (который я уже провалил). Вопрос состоял в том, чтобы сказать, что делает это объявление класса и в каких обстоятельствах его можно использовать?

У меня очень ограниченное понимание универсального программирования, но я понимаю, что «T» — это Type, а «extends» здесь означает, что Type должен был унаследовать «SimpleGenericClass», но я не понимаю «?» в конце и при каких обстоятельствах этот класс потенциально может быть использован для

public abstract class SimpleGenericClass<T extends SimpleGenericClass<?>> {

}

person anonymous    schedule 24.09.2011    source источник
comment
Они оба Simple или Sample, или они должны быть разными классами?   -  person K-ballo    schedule 24.09.2011
comment
Извините, это SimpleGenericClass - я обновил его. Спасибо.   -  person anonymous    schedule 24.09.2011


Ответы (5)


Во-первых, поскольку класс SimpleGenericClass является абстрактным, он должен быть подклассом.

Во-вторых, это универсальный класс, что означает, что где-то внутри класса вы почти наверняка будете использовать универсальный параметр T в качестве типа поля.

public abstract class SimpleGenericClass<T...> {
    T x;
}

Теперь первое интересное здесь то, что T ограничено. Поскольку он объявлен как T extends SimpleGenericClass<?>, он может быть только SimpleGenericClass<?> или некоторым подклассом SimpleGenericClass<?>. Вы также спрашивали о thr ?. Это известно как подстановочный знак, и есть довольно хорошее объяснение этого в Учебник Java по подстановочным знакам. В вашем случае мы бы сказали, что это «SimpleGenericClass of unknown». Это необходимо в Java, потому что, например, SimpleGenericClass<Object> НЕ является суперклассом SimpleGenericClass<String>.

Вторая интересная вещь заключается в том, что, поскольку T является своего рода SimpleGenericClass, ваш класс, скорее всего, определяет рекурсивные структуры. Что мне приходит на ум, так это деревья (подумайте о деревьях выражений), где SimpleGenericClass — это (абстрактный) тип узла, предназначенный для подкласса со всеми видами специализированных типов узлов.

ОБНОВЛЕНИЕ Этот ТАК вопрос о самоограниченных дженериках может быть полезно для вас.

ОБНОВЛЕНИЕ 2

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

public abstract class Node<T extends Node<?>> {
    public abstract T[] getChildren();
}

class NumberNode extends Node {
    int data;
    public Node[] getChildren() {return new Node[]{};}
}

class IdentifierNode extends Node {
    int data;
    public Node[] getChildren() {return new Node[]{};}
}

class PlusNode extends Node {
    NumberNode left;
    NumberNode right;
    public NumberNode[] getChildren() {return new NumberNode[]{};}
}

Здесь хорошо то, что NumberNode[] является допустимым типом возвращаемого значения для PlusNode.getChildren! Имеет ли это значение на практике? Без понятия, но это довольно круто. :)

Это не лучший пример, но вопрос был довольно открытым («Для чего такая вещь может быть использована?»). Конечно, есть и другие способы определения деревьев.

person Ray Toal    schedule 24.09.2011

На самом деле это означает только то, что вы разрешаете пользователю класса SimpleGenericClass параметризовать экземпляры класса с типом T. Однако T не может быть любым типом, а должен быть подтипом SampleGenericClass (или самого SampleGenericClass).

В остальной части кода класса SimpleGenericClass вы можете использовать тип T в сигнатурах методов.

Предположим на секунду, что SimpleGenericClass не является абстрактным. При его использовании вы можете написать:

new SimpleGenericClass<SampleGenericClass<String>>();

т.е. вы параметризуете SimpleGenericClass с помощью SampleGenericClass и SampleGenericClass с помощью String.

person Hendrik    schedule 24.09.2011
comment
Извините, Хендрик, это был не SampleGenericClass во второй раз, а единственный SimpleGenericClass... Я обновил вопрос.. не могли бы вы еще раз взглянуть - person anonymous; 24.09.2011

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

public class C <T extends Number>{

    public void doSomething(T t) {

    }

    public static void main(String... args) {
        //works:
        C<Number> c = new C<Number>();
        c.doSomething(new Number() {
            //Aonimous implementation of number

        });

        //won't work
        //C<Object> c = new C<Object>();

        C<Integer> c2 = new C<Integer>();       
        c2.doSomething(new Integer(1));
        //won't work
        //c2.doSomething(new Number() {

            //Aonimous implementation of number
        //});
    }
}

На данный момент SimpleGenericClass<?> довольно избыточен. Если в этом классе требуется другой общий тип, вы можете иметь более одного (SimpleGenericClass<T extends SimpleGenericClass, T2 extends Whatever>)

person Shivan Dragon    schedule 24.09.2011

По определению он говорит, что SimpleGenericClass может работать с типом <T>, который является подклассом SimpleGenericClass.

Поэтому я предполагаю, что будут некоторые операции, которые будут работать на <T>.

Теперь, чтобы понять, почему можно было бы определить такой шаблон - (на самом деле я мало что могу придумать) может быть сценарий, в котором SimpleGenericClass является абстрактным классом (только что понял, что это соответствует OP: P ) и ожидает, что он может работать на любых конкретных классах?

Ребята, что вы думаете?

person Nrj    schedule 24.09.2011

Я думаю, у вас есть вопрос в этой форме (T вместо ?):

public abstract class SimpleGenericClass<T extends SimpleGenericClass<T>>

Взгляните на этот код:

abstract class Foo<SubClassOfFoo extends Foo<SubClassOfFoo>>
{
    /** subclasses are forced to return themselves from this method */
    public abstract SubClassOfFoo subclassAwareDeepCopy();
}

class Bar extends Foo<Bar> {
    public Bar subclassAwareDeepCopy() {
        Bar b = new Bar();
        // ...
        return b;
    }
}

Bar b = new Bar();
Foo<Bar> f = b;
Bar b2 = b.subclassAwareDeepCopy();
Bar b3 = f.subclassAwareDeepCopy(); // no need to cast, return type is Bar

Трюк, происходящий с Foo<SubClassOfFoo extends Foo<SubClassOfFoo>>, заключается в следующем:

  • Любой подкласс Foo должен предоставить аргумент типа для Foo.
  • Этот аргумент типа должен фактически быть подклассом Foo.
  • Подклассы Foo (например, Bar) следуют идиоме, согласно которой аргумент типа, который они передают Foo, это они сами.

  • У Foo есть метод, который возвращает SubClassOfFoo. В сочетании с приведенной выше идиомой это позволяет Foo сформулировать контракт, в котором говорится: «Любой мой подкласс должен реализовать subclassAwareDeepCopy(), и они должны объявить, что он возвращает этот фактический подкласс».

Другими словами: эта идиома позволяет суперклассу (например, абстрактной фабрике) определять методы, чьи типы аргументов и типы возвращаемых значений относятся к типу подкласса, а не к типу суперкласса.

Трюк реализован, например, в классе Enum JDK:

public abstract class Enum<E extends Enum<E>>

Подробнее см. здесь.

person Andrey    schedule 24.09.2011