Java Generics, поддержка "Специализация"? Концептуальное сходство с шаблонами С++?

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

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

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

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

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

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

Я предполагаю, что как работает Generics в Java, не так ли? Обычное устройство типа я используется в качестве "шаблона", как действовать на все, что я пытаюсь установить там, не так ли? Насколько я понимаю, я могу (или должен?) Поместить некоторые ограничения в аргументы типа: Если я хочу использовать add в своем шаблоне, я должен объявить аргумент типа implement Addable, Верный? Итак, нет "утиной печати" (лучше или хуже).

Теперь, в С++, я могу выбрать specialize для типа, у которого нет add():

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

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

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

Ответ 1

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

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

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

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

/**
 * 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-генераторами и шаблонами С++), поэтому мы не можем просто заменить один из сгенерированных методов на специализированный. p >

Конечно, вы можете создать второй метод 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) (который, в свою очередь, вызывает метод plus), а не перегруженный метод sum(MyX, MyX).

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

Ответ 2

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

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

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

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

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

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

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

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

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

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

caller должен использовать C_Integer вместо C<Integer> для выбора "специализированной" версии.


В утиной печати: Java более строгая в статическом наборе текста - если это не Утка, это не утка.

Ответ 3

HI,

java Обозначает его отличие от шаблона С++.

Пример:

Код Java:

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

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

вы можете создать этот метод таким образом:

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

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

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

Bye