Java Generics, поддержка «Специализация»?Концептуальное сходство с шаблонами C ++? - PullRequest
11 голосов
/ 02 апреля 2011

Я немного знаю, как использовать C ++ - Шаблоны - не эксперт, заметьте. С Java Generics (и Scala, в этом отношении), у меня есть свои трудности. Возможно, потому что я пытаюсь перевести свои знания C ++ в мир Java. Я читал в другом месте: «Они не похожи друг на друга: Java Generics - это только синтаксические примеры сохранения сахара, а шаблоны C ++ - только прославленный препроцессор»: -)

Я совершенно уверен, что оба немного упрощены. Итак, чтобы понять большие и тонкие различия, я попытаюсь начать с Специализация :

В C ++ Я могу разработать Шаблон (класс функций), который действует на любой тип T, который поддерживает мои необходимые операции:

template<typename T>
T plus(T a, T b) { return a.add(b); }

Теперь это потенциально добавляет операцию plus() к любому типу, который может add(). [Note1] [1]

Таким образом, если T поддерживает add(T) мой шаблон работы woll. Если это не так, Компилятор не будет жаловаться, пока я не использую plus(). В питоне мы называем это «печатью утки» : * Если это действует как утка, крякает как утка, это это утка. * (Конечно, при использовании type_traits это немного модифицируется, но пока у нас нет концепций, так работают шаблоны C ++, верно?)

Полагаю, вот как Generics в Java тоже работает, не так ли? Универсальное устройство типа I используется в качестве «шаблона», как работать с чем-либо, что я пытаюсь вставить туда, верно? Насколько я понимаю, я могу (или должен?) Наложить некоторые ограничения на аргументы типа: если я хочу использовать add в своем шаблоне, я должен объявить аргумент типа implement Addable. Правильный? Таким образом, нет «утки» (к лучшему или к худшему).

Теперь в C ++ я могу выбрать специализировать для типа, который не имеет add():

template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }

И даже если все другие типы по-прежнему могут использовать реализацию "по умолчанию", теперь я добавил специальный вариант для MyX - без дополнительных затрат времени выполнения.

Есть ли какой-нибудь механизм Java Generics , который имеет ту же цель? Конечно, в программировании все выполнимо, но я имею в виду концептуально , без каких-либо трюков и магии?

Ответы [ 3 ]

8 голосов
/ 02 апреля 2011

Нет, дженерики в Java не работают таким образом.

С дженериками вы не можете делать ничего, что было бы невозможно без дженериков - вы просто избегаете писать много приведений, иКомпилятор гарантирует, что все безопасно (если вы не получаете некоторые предупреждения и не подавляете их).

Таким образом, для каждой переменной типа вы можете вызывать только методы, определенные в ее границах (без утки).

Кроме того, отсутствует генерация кода (кроме некоторых методов адаптера, которые делегируются методам с другими типами параметров для реализации универсальных типов).Предположим, у вас было что-то вроде этого

/**
 * interface for objects who allow adding some other objects
 */
interface Addable<T> {
   /** returns the sum of this object and another object. */
   T plus(T summand);
}

Тогда мы могли бы создать наш метод sum с двумя аргументами:

public static <T extends Addable<T>> T sum(T first, T second) {
    return first.plus(second);
}

Статический метод компилируется в тот же байт-код, как этот (сдополнительная информация о типе в аннотациях):

public static Addable sum(Addable first, Addable second) {
    return first.plus(second);
}

Это называется стирание типа .

Теперь этот метод можно вызывать для каждой пары из двух элементов добавляемого типа.Пример:

public class Integer implements Addable<Integer> {
    public Integer plus(Integer that) {
       return new Integer(this.value + that.value);
    }

     // private implementation details omitted
}

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

public Object plus(Object that) {
    return this.plus((Integer)that);
}

Этот метод будет вызываться только универсальным кодом с правамитипы, это гарантирует компилятору, если вы не выполняете небезопасных приведений куда-либо - тогда приведение (Integer) здесь поймает ошибку (и сгенерирует ClassCastException).

Метод sum теперь всегда вызывает метод plus первого объекта, пути к этому нет.Нет кода, сгенерированного для каждого возможного аргумента типа (это является ключевым отличием дженериков Java и шаблонов C ++), поэтому мы не можем просто заменить один из сгенерированных методов специализированным.

Конечно, вы можете создать второй sum метод, такой как предлагаемый неопровержимый (с перегрузкой), но он будет выбран, только если вы используете тип MyX непосредственно в исходном коде, а не когдаВы вызываете метод sum из некоторого другого универсального кода, который параметризован с помощью MyX, например:

public static <T extends Addable<T>> product (int times, T factor) {
    T result = factor;
    while(n > 1) {
        result = sum(result, factor);
    }
    return result;
}

Теперь product(5, new MyX(...)) вызовет наш метод sum(T,T) (который, в свою очередь, вызывает* Метод 1048 *, а не какой-либо перегруженный метод sum(MyX, MyX).

(JDK 7 добавляет новый режим диспетчеризации dynamic метода, который позволяет специализацию для каждого аргумента во время выполнения, но это не используется Javaязык, предназначенный только для использования другими языками на основе JVM.)

3 голосов
/ 02 апреля 2011

нет - но ваша конкретная проблема больше связана с перегрузкой.

Нет проблем определить 2 plus методы, подобные этим

<T extends Addable> 
T   plus(T   a, T   b) { .. }

MyX plus(MyX a, MyX b) { .. }

Это работает, даже если MyX является Addable; javac знает, что 2nd plus более специфичен чем 1st plus, поэтому, когда вы вызываете plus с двумя MyX аргументами, выбирается 2nd plus. В некотором смысле Java допускает «специализированную» версию методов:

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

прекрасно работает, если каждый Si является подтипом Ti

Для общих классов мы можем сделать

class C<T extends Number> { ... }

class C_Integer extends C<Integer>{ ... } 

вызывающий должен использовать C_Integer вместо C<Integer>, чтобы выбрать «специализированную» версию.


По типу утки: Java более строг в статической типизации - если это не утка, то это не утка.

2 голосов
/ 02 апреля 2011

HI

java Generics отличается от шаблона C ++.

Пример:

Java-код:

 public <T> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 }

В Java этот код не работает, потому что базовая база - это класс java.lang.Object, поэтому вы можете использовать только метод этого класса.

Вы можете построить этот метис следующим образом:

public <T extends Number> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 } 

в этом случае основой обобщений является класс java.lang.Number, поэтому вы можете использовать Integer, Double, Long ecc ..

Метод "сумма" зависит от реализации java.lang.Number.

Bye

...