在Java 5中引入泛型的原因

时间:2010-08-23 06:24:44

标签: java generics

有人能解释一下Java中引入泛型的原因吗?

据我所知,他们的介绍是为了让您不会意外地将错误类型的对象添加到集合中。

我有点像收集的泛型,但不知道为什么它们也被添加到其他类中。我看过一些让你的头部受伤并且眼睛流血的通用课程。

那么引入泛型的所有原因是什么(主要是非收集)?

TIA!

12 个答案:

答案 0 :(得分:10)

基本上是安全性和便利性。

Java通过一种称为类型擦除的机制实现其泛型,它在编译时有效地用非泛型定义替换泛型定义。这样做的原因是为了保持API兼容性在1.4和1.5之间 - 集合API可能已经更新,但是如果你在1.5中以非通用的方式访问它,那么它的工作方式是相同的。

不同之处在于,如果您希望集合仅包含特定类型的对象,则可以在API中明确地编码该需求,并避免诸如接收包含错误类型对象的列表等问题 - 以及需要必须明确地转换这些对象,使您的代码更易于阅读,并且不易出错。

您将使用泛型的原因与它们与集合一起使用时的原因基本相同 - 当您需要说对象是其他对象的组合时,但是该组合可能有一系列类型可能一旦绑定添加这些新对象,就意味着一种新的复合类型与其他类似的复合材料不同。也就是说,字符串列表在类型上与整数列表相似,但它们不再相互兼容。

这方面的一个例子是在Future等待异步结果的地方。 Future类封装了异步结果的概念,但Future的特定类型(如Future<String>进一步指定了您可以期望的结果类型 - 并使其与{{1 }}

答案 1 :(得分:6)

Java Collection Framework从Java的角度来看并不特别,所以没有理由或者只需要为这个框架添加新的语言功能。

从用户角度看泛型:现在,如果你有一个集合,你就知道它里面有什么类型的对象。这是一个很大的优势。只需比较以下片段,不使用和使用泛型:

使用Java 1.5 +

List<Animal> zoo = new ArrayList<Animal>();
for(Animal animal:zoo) {
  feed(animal);
}

在Java 1.5之前 -

List zoo = new ArrayList();
for (int i = 0; i < zoo.size(); i++) {
  if (!(zoo.get(i) instanceof Animal)) {
    continue;
  } else {
    Animal animal = (Animal) zoo.get(i);
    feed(animal);
  }
}

从用户的角度来看,它更像是破坏眼睛的旧风格。

答案 2 :(得分:4)

添加它们是因为在原始Java中创建真正通用占位符的唯一方法是使用Object类型的字段。这可以采取任何对象。

不幸的是,当你需要它时,它的类型是Object,它没有任何有趣的方法,所以你需要将它转换为你碰巧知道它的类型。这些运行时强制转换是错误的常见来源 - 本质上 - 在运行时发生。

泛型将此移动到编译器,并导致 LOT 减少ClassCastExceptions,从而提高质量。实际的语法有点冗长 - 这里C#关键字'var'会有很多帮助 - 但额外的劳动会带来很多回报。

我相信你必须使用像Haskell这样的强类型语言进行编程才能知道什么是可能的,以及为什么它是可取的。

答案 3 :(得分:3)

泛型为程序员提供了某种类型的安全性。最明显的用途是防止用户无意中将错误的类型传递给函数。它们也可用于避免大量嘈杂的铸造。

这是一个非收集示例,可以从泛型中受益。如果我们有很多与模型相关的视图,我们可能会定义一个表示视图的接口。该接口可能具有getModel()方法:

public interface View {
   Model getModel();
}

任何调用该方法的人都必须强制转换结果:

MyView view = new MyView();
MyModel model = (MyModel) view.getModel();

因此我们使用as generic指定接口:

public interface View<M extends Model> {
   M getModel();
}

一个名为MyView的具体类

class MyView implements View<MyModel> {
  MyModel getModel();
}

然后客户端代码更清洁:

MyView view = new MyView();
MyModel model = view.getModel();

通过精心设计,泛型可以使代码更具可读性。但是,泛型类型信息不会被编译到类文件中,因此在运行时仍有可能被滥用。

答案 4 :(得分:1)

您想要进行通用编程的任何地方; - )

是的,你不介意(太多)关于某个对象的类型,但是想要限制它的类型才能正常运行。

成像管道和过滤器。想象一下,每个管道都有一个特定的输出。一个输出字节,其他字符串和其他数字。每个管道都有其特定的类型会很好,你只能将X输出的管道挂钩到X输入的管道。

Byte -> Filter -> String -> Filter -> Integer

因此,您的第一个过滤器将为Filter<Byte, String>,其hookOutputPipe将收到Pipe<String>

也许你的眼睛在参数化类型上流血,但在某些情况下,它对于API开发人员来说是一个非常好的联盟。如果你开始在适当的情况下使用它们,你将感觉它们的用处。

来自现实世界的一个例子:GWT中的事件类型和处理程序类型。

答案 5 :(得分:1)

Two main objectives of Generics concepts are

1. To provide type safety to the collection so that they can hold only particular type of object

2.To resolve type casting problems

ArrayList<String> ob=new ArrayList<String>();

ob.add("shail");
ob.add("ball");
ob.add(10);  //compile time error

At the time of retrival does not require type casting

String s1=ob.get(0);

答案 6 :(得分:1)

JDK 5.0引入了Java编程语言的几个新扩展,其中之一就是泛型的引入。

泛型提供了类型的抽象。以下是此类的典型用法:

    List l1 = new LinkedList(); // 1
    l1.add(new Integer(10)); // 2
    Integer x = (Integer) l1.iterator().next(); // 3

第3行的演员阵容有点烦人。通常,程序员知道将哪种数据放入特定列表中。但是,演员是必不可少的。为确保赋值为Integer类型的变量是类型安全的,需要强制转换。

当然,演员不仅会引入混乱。它还引入了运行时错误的可能性,因为程序员可能会弄错。

如果程序员可以将列表标记为限制包含特定数据类型,该怎么办?这是仿制药背后的核心理念。

以下是使用泛型的上述程序的示例:

    List <Integer> l1 = new LinkedList <Integer>(); // 1
    l1.add(new Integer(10)); // 2
    Integer x = l1.iterator().next(); // 3

注意变量l1的类型声明。它指定这不仅仅是一个任意的List,而是一个List of Integer,写成List。我们说List是一个带有类型参数的通用接口 - 在本例中是Integer。我们还在创建列表对象时指定了一个类型参数。

enter link description here

答案 7 :(得分:0)

泛型在集合类中确实特别有用,但它们的用处并不仅限于此。我无法想象如何将通用功能提供给集合类,而不是普遍可用 - 泛型是一种基本语言功能,而不是集合的特定修补程序。您是否希望编译器具有以下逻辑:

if this is a collection class
    allow generics

并且在我可以编写自己的集合之后,编译器如何发现它有一个集合类。

像许多尖锐的工具一样,泛型可能会被误用和过度使用,这并不会使工具本身成为一件坏事。

答案 8 :(得分:0)

让我们说,我们想编写一个方法来找到两个可比对象的最大值,类似于Math.max():

public static <T extends Comparable<T>> T max(T arg0, T arg1) {
    return arg0.compareTo(arg1) > 0 ? arg0 : arg1;
}

现在你可以调用这样的方法,

int maxNum = max(5 ,3);
String maxString = max("AAA", "BBB");
File maxFile = max(new File("path/to/file1"), new File("path/to/file2"));

尝试在没有泛型的情况下定义此功能。这里推断了返回类型,你根本不需要任何类型的转换。此外,在编译时检查类型,而不是在运行时导致ClassCastException。

答案 9 :(得分:0)

  • 消除明确的演员
  • 在编译时进行更强的类型检查

取消明确投射

没有泛型:

List a = new LinkedList();
a.add(new Integer(5));
Integer i = (Integer)a.get(0);  //Manual type conversion

使用泛型:

List<Integer> a = new LinkedList();
a.add(new Integer(5));
Integer i = a.get(0);

这限制了列表首先可以包含的数据类型。

在编译时强化类型检查

没有泛型:

List a = new LinkedList();
a.add("asia");
Integer i = (Integer)a.get(0); //compiles successfully

以上代码成功编译但在运行时抛出ClassCastException

使用泛型:

List<String> a = new LinkedList();
a.add("asia");
Integer i = a.get(0);   //compile time error

上面的代码将在编译时中断,并将我们从运行时错误中解救出来。

  

Java Generics只是一个编译时的概念。在运行时所有类型   信息被删除,这称为类型擦除。

答案 10 :(得分:0)

基本上通过在编译时检测类型错误来防止错误。 泛型允许您遵循 DRY原则,同时保持类型安全

引用Oracle's Java documentation on Generics

  

泛型通过制造更多错误来增加代码的稳定性   在编译时可检测到。

Generics tutorial by Gilad Bracha的介绍:

  

净效应,特别是在大型节目中,提高了可读性   和稳健性。

不仅集合可以使用泛型,您可以轻松地想到装饰器,代理和更多案例。

答案 11 :(得分:0)

来自JavaDocs

简而言之,泛型在定义类,接口和方法时使类型(类和接口)成为参数。与方法声明中使用的更熟悉的形式参数非常相似,类型参数为您提供了一种使用不同输入重复使用相同代码的方法。区别在于形式参数的输入是值,而类型参数的输入是类型。

使用泛型代码比非泛型代码有很多好处:

  1. 更强类型在编译时检查。 Java编译器适用性强 对通用代码进行类型检查,如果代码违反则发出错误 类型安全。修复编译时错误比修复更容易 运行时错误,可能很难找到。
  2. 消除强制转换。以下不带泛型的代码段 需要强制转换:

    列表列表= new ArrayList(); list.add(“ hello”);     字符串s =(字符串)list.get(0);

    重新编写以使用泛型时, 该代码不需要强制转换:

    列表列表=新     数组列表(); list.add(“ hello”);字符串s = list.get(0); //     没有演员

  3. 使程序员能够实现通用算法。通过使用 泛型,程序员可以实现适用于 不同类型的集合,可以自定义并且类型安全 而且更易于阅读。