Обычным способом было бы определить его следующим образом:
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);
}