Создание экземпляра универсального расширения абстрактного класса

#java #generics #parameters #abstract #instantiation

#java #обобщения #параметры #аннотация #создание экземпляра

Вопрос:

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

 public abstract class Genotype {
private ArrayList<Gene> genotype = new ArrayList<Gene>();

//...

public Genotype(ArrayList<Gene> genotype) {
    setGenotype(genotype);
    setGenotypeLength(genotype.size());
}

public abstract Phenotype<Gene> getPhenotype();

public abstract void mutate();

//...
}
  

Этот класс предназначен для расширения, и подкласс, очевидно, обеспечивает реализацию getPhenotype() и mutate() . Однако у меня также есть второй класс, который принимает два объекта генотипа в качестве параметров и возвращает ArrayList, содержащий объекты генотипа. Поскольку на данный момент я не знаю тип объектов расширенного генотипа, мне нужно использовать общий параметр следующим образом:

 public class Reproducer {

//...

    private <G extends Genotype> ArrayList<Genotype> crossover(G parent1, G parent2) {
        ArrayList<Genotype> children = new ArrayList<Genotype>();

        ArrayList<Gene> genotypeOne = ArrayListCloner.cloneArrayList(parent1.getGenotype());
        ArrayList<Gene> genotypeTwo = ArrayListCloner.cloneArrayList(parent2.getGenotype());

        //one point crossover
        int p = gen.nextInt(genotypeOne.size());

        for (int i = 0; i < p; i  ) {
            genotypeOne.set(i, genotypeOne.get(i));
            genotypeTwo.set(i, genotypeTwo.get(i));
        }
        for (int i = p; i < 10; i  ) {
            genotypeOne.set(i, genotypeTwo.get(i));
            genotypeTwo.set(i, genotypeOne.get(i));
        }

        children.add(new G(genotypeOne)); //THROWS ERROR: Cannot instantiate the type G
        children.add(new G(genotypeTwo)); //THROWS ERROR: Cannot instantiate the type G

        return children;
    }
}
  

Однако, поскольку мне нужно вернуть два объекта типа G в ArrayList, у меня явно есть проблема, когда я не могу создать экземпляр новых объектов генотипа, потому что они 1. общие типы и, предположительно, 2. абстрактные.

Это может быть плохой способ объединить все вместе, но если у кого-нибудь есть решение, это было бы здорово. Спасибо.

Ответ №1:

Я бы предложил использовать фабричный метод в вашем Genotype классе

 public abstract class Genotype {
     public abstract GenoType newInstance();
}
  

Комментарии:

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

2. @carnun, нет, этого не должно быть. Просто вызовите его в своем GenoType экземпляре, например, GenoType g = parent1.newInstance() . Вы также можете добавить аргументы, если хотите.

3. Это ВЕЛИКОЛЕПНО! Спасибо, Йохан. Решена очень похожая проблема и улучшен мой код в 100 раз. У меня был окончательный массив обобщений в абстрактном классе, и мой компилятор не переставал жаловаться.

Ответ №2:

Вы могли бы использовать абстрактный фабричный шаблон. Поскольку вам нужен только один фабричный метод на вашем заводе, это, возможно, вырожденный случай, но это может быть то, что вам нужно. Вы должны передать экземпляр factory своему Reproducer объекту, возможно, в качестве аргумента какого-либо метода. Это некрасиво, но, по крайней мере, теперь у вас есть абстракция над созданием объекта.

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

 public static <G extends Gene> List<? extends G> mutate( List<G> genotype ) { ... }
public static <G extends Gene> List<List<? extends G>> crossover( List<G> p1, List<G> p2 ) { ... }
  

В качестве дополнительного замечания: предпочитайте объявлять типы интерфейсов, а не типы классов; в вашем примере вы не используете List интерфейс.

В качестве последнего примечания: в вашем примере вам действительно не нужен универсальный тип. Если вы объявляете типы параметров, Genotype вы получаете то же решение (с той же проблемой). Параметр типа X принимает любой объект типа X (включая подтипы!) В качестве аргумента.