Метод имеет такое же стирание, как и другой метод в типе

Почему это не законно иметь эти два метода в одном классе?

class Test{
   void add(Set<Integer> ii){}
   void add(Set<String> ss){}
}

Я получаю compilation error

Метод add (Set) имеет то же стирание add (Set), что и другой метод в типе Test.

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

Я вижу, что во многих случаях логика этих двух методов будет очень схожей и может быть заменена одним

public void add(Set<?> set){}

но это не всегда так.

Это лишнее раздражает, если вы хотите иметь два constructors, которые принимают эти аргументы, потому что тогда вы не можете просто изменить имя одного из constructors.

Ответ 1

Это правило предназначено для предотвращения конфликтов в устаревшем коде, который по-прежнему использует необработанные типы.

Вот иллюстрация того, почему это не было разрешено, из JLS. Предположим, что до того, как дженерики были введены в Java, я написал несколько код вроде этого:

class CollectionConverter {
  List toList(Collection c) {...}
}

Вы расширяете мой класс, например:

class Overrider extends CollectionConverter{
  List toList(Collection c) {...}
}

После введения дженериков я решил обновить свою библиотеку.

class CollectionConverter {
  <T> List<T> toList(Collection<T> c) {...}
}

Вы не готовы делать какие-либо обновления, поэтому оставите свой класс Overrider. Чтобы правильно переопределить метод toList(), разработчики языка решили, что необработанный тип "переопределяет эквивалент" для любого генерируемого типа. Это означает, что, хотя ваша подпись метода больше не является формальной моей подписью суперкласса, ваш метод все еще переопределяет.

Теперь пройдёт время, и вы решили, что готовы обновить свой класс. Но вы немного испортили, и вместо редактирования существующего, необработанного метода toList() вы добавляете новый метод следующим образом:

class Overrider extends CollectionConverter {
  @Override
  List toList(Collection c) {...}
  @Override
  <T> List<T> toList(Collection<T> c) {...}
}

Из-за переопределенной эквивалентности типов raw оба метода в допустимой форме переопределяют метод toList(Collection<T>). Но, конечно, компилятор должен решить один метод. Чтобы устранить эту двусмысленность, классам не разрешено иметь несколько методов, переопределяющих эквивалент &mdash, то есть несколько методов с теми же параметрами после стирания.

Ключ в том, что это правило языка, предназначенное для обеспечения совместимости со старым кодом с использованием необработанных типов. Это не ограничение, требуемое стиранием параметров типа; потому что разрешение метода происходит во время компиляции, добавление общих типов в идентификатор метода было бы достаточно.

Ответ 2

Java generics использует стирание типов. Бит в угловых скобках (<Integer> и <String>) удаляется, поэтому вы получите два метода, которые имеют идентичную подпись (add(Set), которые вы видите в ошибке). Это не допускается, потому что время выполнения не знает, что использовать для каждого случая.

Если Java когда-либо получает повторно генерируемые дженерики, тогда вы можете сделать это, но это, вероятно, вряд ли сейчас.

Ответ 3

Это связано с тем, что Java Generics реализованы с помощью Type Erasure.

Ваши методы будут переведены во время компиляции на что-то вроде:

Разрешение метода происходит во время компиляции и не учитывает параметры типа. (см. ответ erickson)

void add(Set ii);
void add(Set ss);

Оба метода имеют одну и ту же подпись без параметров типа, поэтому ошибка.

Ответ 4

Проблема в том, что Set<Integer> и Set<String> фактически рассматриваются как Set из JVM. Выбор типа для Set (String или Integer в вашем случае) - это только синтаксический сахар, используемый компилятором. JVM не может различать Set<String> и Set<Integer>.

Ответ 5

Возможно, компилятор переводит Set (Integer) в Set (Object) в java-байтовый код. Если это так, Set (Integer) будет использоваться только на этапе компиляции для проверки синтаксиса.

Ответ 6

Определите один метод без типа типа void add(Set ii){}

Вы можете указать тип при вызове метода по вашему выбору. Он будет работать для любого типа набора.