Могу ли я предоставить методы, которые используют универсальные типы, где универсальный тип всегда является классом? - PullRequest
2 голосов
/ 08 июня 2011

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

Я считаю, что лучшее - это способНапример, если у меня есть класс

public abstract class Genome
{
    abstract public Genome randomize();
    abstract public Genome mutate();
    abstract public Genome crossOver(Genome genome);
}

Могу ли я убедиться, что его подклассы всегда будут реализовываться с использованием обобщенных значений

public class GenomeSubclass extends Genome
{
    public GenomeSubclass randomize();
    // etc...
}

вместо абстрактных методов, предусмотренных в контракте?

Ответы [ 2 ]

3 голосов
/ 08 июня 2011

Обычным способом было бы определить его следующим образом:

public abstract class Genome<T extends Genome<T>> {
    abstract public T randomize();
    abstract public T mutate();
    abstract public T crossOver(T genome);
}

public class GenomeSubclass extends Genome<GenomeSubclass>
{
    public GenomeSubclass randomize();
    // etc...
}

Этот подход используется в Comparable (или, по крайней мере, в большинстве случаев используется для Comparable) и Enum.

Конечно, это не мешает создавать подклассы для других подклассов.


Изменить, чтобы детализировать мой комментарий:

Вы не можете иметь оба

class GenomeSubclass extends Genome {
    public GenomeSubclass crossOver(GenomeSubclass other);
}

и

class GenomeSubSubclass extends GenomeSubclass {
    public GenomeSubclass crossOver(GenomeSubSubclass other);
}

, в то время как оба реализуют один и тот же метод Генома.Каждый подтип GenomeSubSubclass должен реализовывать метод crossOver(GenomeSubclass) и не может дополнительно ограничивать аргумент.

Конечно, вы можете предоставить GenomeSubclass собственный параметр типа:

public class GenomeSubclass<T extends GenomeSubclass<T>> extends Genome<GenomeSubclass<T>>
{
    public GenomeSubclass<T> randomize();
    // etc...
}

но тогда вы не сможете использовать этот класс напрямую, не возвращаясь к необработанному типу.(Вам придется написать GenomeSubclass<GenomeSubclass<GenomeSubclass<...>>>, что на самом деле невозможно. Или я как-то здесь заблуждаюсь.)


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

public abstract class GenomeA extends Genome<GenomeA> {}

и затем

public class AImpl1 extends GenomeA {
   public GenomeA randomize();
   public GenomeA mutate();
   public GenomeA crossOver(GenomeA other);
}

public class AImpl2 extends GenomeA {
   public GenomeA randomize();
   public GenomeA mutate();
   public GenomeA crossOver(GenomeA other);
}
1 голос
/ 08 июня 2011

К сожалению, в Java нет типа This, в отличие от this объекта.

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

Теперь, зачем тебе это?Единственное известное использование - для цепочки методов.Если у вас другой вариант использования, скажите, это было бы очень интересно.

Если это только для цепочки методов, лично я не считаю отсутствие This большим делом.

РЕДАКТИРОВАТЬ : crossover(This) является интересной темой.

Скажем, у нас есть суперкласс G и подклассы A и B.Предположим, что A может только crossover(A), а B только с B.Является ли операция crossover обычной операцией для G?

Не в нашей системе типов.Конечно, мы можем считать два crossover() связанными, но это отношение не может быть выражено в нашей системе типов.Требование, чтобы X объявлял метод crossover(X), не может быть применено.(Дженерики тоже не могут применять его; подумайте X extends G<Y>)

Здесь необходимо перейти на "экстралингвистический", использовать другой язык (английский) и другой компилятор (наши глазные яблоки) для выражения и применения отношения.Или изобрести новый статический анализатор, вооруженный синтаксисом, способным выражать такие отношения.Для выполнения необычного метода диспетчеризации может потребоваться отражение во время выполнения.

Возможно что-то вроде этого:

/** verbal contract: subclass X must declare method `X crossover(X)` **/
class G

    // no explicit crossover() declaration in G

    static <X extends G> X crossover(X x1, X x2)
        X1 = x1.getClass(), X2 = x2.getClass();
        check: X2 is assignable to X1
        check: X1 has method `X1 crossover(X1)`
        invoke dynamic x1.crossover(x2)

class A

    A crossover(A)
...