使用泛型的可重用接口

时间:2011-02-07 18:50:04

标签: java generics interface code-reuse

我想创建一个引用使用接口的服务类型的类。该服务可以有不同的实现。不同的实现将处理不同类型的请求。在过去,我会定义一个类似这样的界面:

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 个答案:

答案 0 :(得分:4)

泛型的要点是确保您的代码更可靠(就类型安全而言)。使用泛型,您可以在编译时而不是运行时找到类型不兼容性。当您将接口定义为I_Test<T extends Enum>时,您基本上是说需要根据特定类型对接口进行通用化。这就是Java给你一个警告的原因。

如果你做了类似Map myMap = new HashMap<string>();的事情,你会得到同样的警告。

在Java中,您实际上指定了类型,并且它们不是从RHS上的内容推断出来的(除非您执行Integer i = 1之类的操作,但这是自动装箱)。由于您对接口进行了通用化,因此在使用该接口声明某些内容时,需要指定要使用的类型(进行泛化)。

当您实例化泛型类型时,编译器将使用称为“类型擦除”的东西来转换这些类型。这里,编译器删除与类型参数和类型参数关联的所有信息。 Java这样做是为了保持与Java之前编写的旧代码的兼容性。

因此在编译期间I_Test<Enum_1>实际上已转换为原始类型I_Test。使用原始类型通常被认为是不好的做法(因此,“烦人的警告”)。编译器告诉你它没有足够的信息来执行类型检查,因此无法确保类型安全(因为你使用了原始类型)。

要了解有关泛型的更多信息,请查看以下内容:

答案 1 :(得分:1)

泛型 关于编译时警告。如果您不想要它们,请不要使用它们。

话虽如此,您可以创建不同的非通用子接口,例如:

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

然后将您的班级声明为

public class Test_1 implements Enum_1_Test

但我不相信这是非常有用的。根据经验,如果你有一个适用于许多输入类型的实现,并且如果你想为每个输入类型单独实现,那么你想使用泛型。

答案 2 :(得分:1)

原始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