Многоразовый интерфейс, который использует дженерики - PullRequest
2 голосов
/ 07 февраля 2011

Я хочу создать отдельный класс, который ссылается на тип сервиса с использованием интерфейса. Сервис может иметь разные реализации. Разные реализации будут обрабатывать разные типы запросов. Раньше я определял интерфейс примерно так:

public interface I_Test
{
    public String get(String key, Enum type);
}

и реализовать это так:

public class Test_1 implements I_Test
{
    public String get(String key, Enum type)
    {
        Enum_1 t1 = (Enum_1)type;

        switch(t1)
        {
            case NAME:
                return "Garry";
            case DOB:
                return "1966";
            default:
                throw new IllegalArgumentException("Unkown type [" + type + "]");
        }
    }
}

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

Я надеялся, что дженерики могут решить эту проблему, поэтому я сделал это:

public interface I_Test<T extends Enum>
{
    public String get(String key, T type);
}

и это:

public class Test_1 implements I_Test<Enum_1>
{
    public String get(String key, Enum_1 type)
    {
        switch(type)
        {
            case NAME:
                return "Garry";
            case DOB:
                return "1966";
            default:
                throw new IllegalArgumentException("Unkown type [" + type + "]");
        }
    }
}

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

I_Test<Enum_1> t1 = new Test_1();

Это действительно беспокоит меня, потому что весь смысл создания интерфейса I_Test заключался в том, что я мог использовать разные реализации, но, похоже, мне нужно привязаться к определенному типу во время компиляции, чтобы избежать этого предупреждения!

Есть ли способ написать повторно используемый интерфейс, который использует дженерики без этого раздражающего предупреждения?

Ответы [ 3 ]

4 голосов
/ 07 февраля 2011

Суть обобщений заключается в обеспечении того, чтобы ваш код был более надежным (с точки зрения безопасности типов).С помощью дженериков вы можете узнать о несовместимости типов во время компиляции, а не во время выполнения.Когда вы определили свой интерфейс как I_Test<T extends Enum>, вы в основном говорите, что вам do нужен интерфейс, который будет обобщен в соответствии с конкретным типом.Вот почему Java выдает вам предупреждение.

Вы получите то же самое предупреждение, если сделаете что-то подобное Map myMap = new HashMap<string>();.

В Java вы фактически указываете типы, а они не являютсявыводится из того, что на RHS (если вы не делаете что-то вроде Integer i = 1, но это автобокс).Поскольку вы обобщали свой интерфейс, когда вы объявляете что-либо с использованием этого интерфейса, вам необходимо указать тип для использования (для генерирования).

Когда вы создаете экземпляр универсального типа, компилятор будет переводить эти типы с помощью чего-то, называемого"стирание типа".Здесь компилятор удаляет всю информацию, связанную с параметрами типа и аргументами типа.Java делает это для обеспечения совместимости со старым кодом, который был написан до того, как в Java появились обобщения.

Таким образом, I_Test<Enum_1> фактически транслируется в необработанный тип I_Test во время компиляции.Использование необработанного типа обычно считается плохой практикой (следовательно, «раздражающее предупреждение»).Компилятор говорит вам, что у него недостаточно информации для проверки типов и, следовательно, он не может обеспечить безопасность типов (потому что вы использовали необработанный тип).

Чтобы узнать больше о дженериках, взгляните наследующее:

1 голос
/ 07 февраля 2011

Необработанный I_Test поддерживает любой тип перечисления в качестве аргумента, в то время как реализация Test_1 поддерживает только ограниченное подмножество (Enum_1), потому что Test_1 указан как реализующий I_Test только для одного типа перечисления.

Вот пример того, почему компилятор выдает предупреждение, следующий код компилируется, поскольку необработанный тип I_Test принимает любое перечисление, однако, поскольку Test_1 поддерживает только Enum_1, он выдаст исключение приведения класса.

enum MyEnum{A}
I_Test t1 = new Test_1();//warning here
t1.get("",MyEnum.A);//Exception at runtime, but compiles fine

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

enum MyEnum{A}
I_Test<Enum_1> t1 = new Test_1();
t1.get("",MyEnum.A);//Does not compile
1 голос
/ 07 февраля 2011

Generics означает о предупреждениях времени компиляции.Если они вам не нужны, не используйте их.

Сказав это, вы можете создавать различные неуниверсальные подынтерфейсы, например:

public interface Enum_1_Test extends I_Test<Enum_1> {
  ...
}

И затем объявить свойкласс как

public class Test_1 implements Enum_1_Test

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

...