Обобщения и специализированные конструкторы Java - PullRequest
9 голосов
/ 12 декабря 2011

(Возможно, это дубликат, но я не смог его найти - не стесняйтесь указывать на это)

Рассмотрим следующий класс Java:

public class A<T0, T1> {
    public A(T0 t0, T1 t1) {
        ...
    }
}

Создать этот класс легко, используя что-то вроде new A<Integer, String>(1, "X").

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

Если бы A не использовал универсальные шаблоны, общим расширением был бы дополнительный конструктор без второго аргумента:

public class A {
    public A(int t0, String t1) {
        ...
    }

    public A(int t0) {
        this(t0, new String("X"));
    }
}

К сожалению, это не представляется возможным для класса, который использует дженерики - по крайней мере, без принудительного приведения:

    public A(T0 t0) {
        this(t0, (T1)(...));
    }

Причина?Хотя этот конструктор принимает только один аргумент, он по-прежнему использует параметры типа два , и нет никакого способа узнать a priori , что независимо от типа T1 пользователь класса предоставитсовместим со значением по умолчанию, используемым в конструкторе.

Несколько более элегантное решение предполагает использование подкласса:

 public class B<T0> extends A<T0, String> {
     ...
 }

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

  • Есть ли способ объявить конструктор, который приводит один или несколько параметров типа к определенному типу?Что-то с теми же эффектами, что и при использовании подкласса, но без хлопот?

  • Есть ли что-то принципиально неправильное в моем понимании обобщений и / или моего дизайна?Или это актуальная проблема?

Ответы [ 6 ]

6 голосов
/ 12 декабря 2011

Самый простой способ - просто добавить статический метод создания.

public static <T0> A<T0,String> newThing(T0 t0) {
    return new A<T0,String>(t0, "X");
}

(Возможно, выберите имя, подходящее для конкретного использования. Обычно нет необходимости в new String("...").)

ОтJava SE 7, вы можете использовать алмаз:

A<Thing,String> a = new A<>(thing);
2 голосов
/ 12 декабря 2011

Насколько я понимаю, вы хотите иметь второй конструктор, который (если он будет вызван) заставит универсальный тип T1 быть String.

Тем не менее, дженерики указываются ДО того, как вы вызовете конструктор.

Этот второй конструктор, если он допустим, может позволить кому-то сделать это:

B<Integer, Integer> b = new B<Integer, Integer>(5);

Ошибка здесь в том, что вы указали второй универсальный тип как Integer ДО вызова конструктора. И тогда конструктор теоретически определит второй универсальный тип как String. Вот почему я считаю, что это не разрешено.

1 голос
/ 12 декабря 2011

Вы можете квалифицировать универсальные типы, т.е.

A<T0, T1 super MyDefaultType> {
   public A(T0 t0) {
    this(t0, new MyDefaultType());
   }
}

Вы не можете использовать T1 extends MyDefaultType, поскольку, если вы определите подкласс, экземпляр MyDefaultType не будет несовместим с типом T1.

1 голос
/ 12 декабря 2011

«Большинство экземпляров» - это основная проблема.

Либо T1 является параметризованным типом, либо нет. Конструктор с одним аргументом предполагает оба. В этом и заключается проблема.

Решение подкласса решает проблему, заставляя все экземпляров удовлетворять T1 = String.

Именованный метод конструктора / фабрики также решит проблему, обеспечив T1 = String.

    public static <T0> A<T0,String> makeA( T0 t0 ) {
       return new A<T0,String>( t0, "foo" );
    } 
0 голосов
/ 12 декабря 2011

Подкласс это. Насколько мне известно, это одна из замечательных особенностей ООП. Наслаждайся этим. Дисковое пространство дешево.

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

0 голосов
/ 12 декабря 2011

Есть ли способ объявить конструктор, который приводит один или несколько параметров типа к определенному типу?Что-то с теми же эффектами, что и при использовании подкласса, но без хлопот?

Я считаю, что это невозможно.Думать об этом.Разработчик определяет класс, который может быть универсальным, то есть тип параметра определяется при создании объекта.Как разработчик может определить конструктор, который заставляет пользователя использовать конкретный тип параметра?

РЕДАКТИРОВАТЬ: Если вам это нужно, вы должны создать фабрику или метод фабрики, который создает экземпляры этого класса с предопределенным типом параметра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...