在Java 5中引入generics的原因

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

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

我有点喜欢收集的generics,但不知道为什么它们也被添加到其他类中。 我见过一些让你的头部疼痛,眼睛流血的通用课程。

那么generics的所有原因是什么(主要是非收集)?

TIA!

基本上类型安全和方便。

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

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

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

其中一个例子是在Future中等待异步结果。 Future类封装了异步结果的概念,但Future的特定类型(例如Future futher)指定了您可以期望的结果类型 – 并使其与Future

从Java的角度来看,Java Collection Framework并不特别,因此没有理由或者只需要为此框架添加新的语言function。

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

使用Java 1.5+

 List zoo = new ArrayList(); 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); } } 

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

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

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

generics将其移至编译器,并导致更少的ClassCastExceptions,从而提高质量。 实际的语法有点冗长 – 这里C#关键字’var’会有很多帮助 – 但额外的劳动会带来很多回报。

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

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

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

 public interface View { Model getModel(); } 

任何调用该方法的人都必须施加一个痛苦的结果:

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

所以我们使用as generic指定接口:

 public interface View { M getModel(); } 

一个名为MyView的具体类

 class MyView implements View { MyModel getModel(); } 

然后客户端代码更清洁:

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

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

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

它是,你不介意(太多)关于某个对象的类型,但希望限制其类型以正常运行。

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

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

所以你的第一个Filter将是Filter ,它的hookOutputPipe将收到一个Pipe

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

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

 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 ob=new ArrayList(); 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); 

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

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

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

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

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

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

以下是使用generics的上述给定程序的示例:

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

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

在此输入链接描述

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

 if this is a collection class allow generics 

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

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

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

 public static > 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")); 

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

  • 消除显性演员
  • 在编译时更强大的类型检查

消除显性演员

没有generics:

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

使用generics:

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

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

在编译时更强大的类型检查

没有generics:

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

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

使用generics:

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

上面的代码将在编译本身中断,并使我们免于运行时错误。

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

通过在编译时检测类型错误,本质上是为了防止错误 。 generics允许您遵循DRY原则,同时保持类型安全

引用关于generics的Oracle Java文档 :

generics通过在编译时检测到更多错误来增加代码的稳定性。

Gilad Bracha介绍了generics教程 :

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

不仅集合可以使用generics,您可以轻松地想到装饰器,代理和更多情况。