Java Genrics是Java 5中引入的最重要的功能之一。如果您一直在使用Java Collections(/community/tutorials/collections-in-java-tutorial)和版本5或更高版本,我确信您已经使用了它。 **Java Genrics with collection classes是非常简单的,但它提供了比仅仅创建集合类型的更多功能。
下面我们将看看Java中的通用元素。
简介:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java Generic 类型:Java
1、Java中的基因
在 Java 5 中添加了 Generics 以提供 compile-time 类型检查,并删除在与收藏类一起工作时常见的ClassCastException
风险。
1List list = new ArrayList();
2list.add("abc");
3list.add(new Integer(5)); //OK
4
5for(Object obj : list){
6 //type casting leading to ClassCastException at runtime
7 String str=(String) obj;
8}
上面的代码编译得很好,但在运行时丢弃了 ClassCastException,因为我们正在试图将列表中的对象投放到 String,而其中一个元素类型为 Integer。
1List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>();
2list1.add("abc");
3//list1.add(new Integer(5)); //compiler error
4
5for(String str : list1){
6 //no type casting needed, avoids ClassCastException
7}
请注意,在创建列表时,我们已经指定了列表中的元素类型为 String。因此,如果我们试图在列表中添加任何其他类型的对象,该程序会引发编译时间错误。
二、Java通用类
我们可以用通用类型来定义自己的类型。通用类型是通过类型进行参数化的类型或界面。我们使用角度支架(<>)来指定类型参数。
1package com.journaldev.generics;
2
3public class GenericsTypeOld {
4
5 private Object t;
6
7 public Object get() {
8 return t;
9 }
10
11 public void set(Object t) {
12 this.t = t;
13 }
14
15 public static void main(String args[]){
16 GenericsTypeOld type = new GenericsTypeOld();
17 type.set("Pankaj");
18 String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
19 }
20}
请注意,在使用此类时,我们必须使用类型铸造,它可以在运行时生成 ClassCastException。
1package com.journaldev.generics;
2
3public class GenericsType<T> {
4
5 private T t;
6
7 public T get(){
8 return this.t;
9 }
10
11 public void set(T t1){
12 this.t=t1;
13 }
14
15 public static void main(String args[]){
16 GenericsType<String> type = new GenericsType<>();
17 type.set("Pankaj"); //valid
18
19 GenericsType type1 = new GenericsType(); //raw type
20 type1.set("Pankaj"); //valid
21 type1.set(10); //valid and autoboxing support
22 }
23}
注意在主要方法中使用 GenericsType 类别. 我们不需要进行类型投放,我们可以在运行时删除 ClassCastException. 如果我们在创建时不提供类型,编译器会发出警告,即GenericsType 是原始类型。 参考一般类型 GenericsType<T> 应该被参数化
。 当我们不提供类型时,类型变成对象
,因此它允许 String 和 Integer 对象。
** 提示**:我们可以使用@SuppressWarnings
(rawtypes
)的注释来抑制编译器的警告,请查看 (java注释教程)(/社区/教程/java注释)。
也注意它支持 java autoboxing 。
Java 通用接口
相似界面是通用界面中的一个很好的例子,它被写成:
1package java.lang;
2import java.util.*;
3
4public interface Comparable<T> {
5 public int compareTo(T o);
6}
类似的方式,我们可以创建一般界面在java. 我们也可以有多个类型参数,就像在地图界面. 再次,我们可以提供参数值的参数化类型,例如,‘新 HashMap<String,列表
Java 一般类型
Java 通用型号命名公约有助于我们轻松地理解代码,并且有一个命名公约是 Java 编程语言的最佳实践之一,因此通用型号也带来了自己的命名公约。
- E - 元素(Java Collections Framework 广泛使用,例如 ArrayList, Set 等)
- K - 关键(在地图中使用)
- N - 数字
- T - 类型
- V - 值(在地图中使用)
- S,U,V 等 - 2nd, 3rd, 4th类型
5. Java 通用方法
有时我们不希望整个类被参数化,在这种情况下,我们可以创建 Java 通用方法. 由于 constructor是一种特殊类型的方法,我们也可以在 constructors 中使用通用方法。
1package com.journaldev.generics;
2
3public class GenericsMethods {
4
5 //Java Generic Method
6 public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
7 return g1.get().equals(g2.get());
8 }
9
10 public static void main(String args[]){
11 GenericsType<String> g1 = new GenericsType<>();
12 g1.set("Pankaj");
13
14 GenericsType<String> g2 = new GenericsType<>();
15 g2.set("Pankaj");
16
17 boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);
18 //above statement can be written simply as
19 isEqual = GenericsMethods.isEqual(g1, g2);
20 //This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
21 //Compiler will infer the type that is needed
22 }
23}
注意 isEqual 方法签名显示语法以在方法中使用通用类型. 另外,注意如何在我们的 java 程序中使用这些方法. 我们可以在调用这些方法时指定类型,或者我们可以像正常方法一样召唤它们. Java 编译器足够聪明来确定要使用的变量类型,这种设施被称为 type inference。
Java Generics 边界类型参数
假设我们想要限制可在参数化类型中使用的对象类型,例如在比较两种对象的方法中,我们希望确保所接受的对象是可比较的。
1public static <T extends Comparable<T>> int compare(T t1, T t2){
2 return t1.compareTo(t2);
3 }
这些方法的召唤类似于无限方法,但如果我们试图使用任何类别,它会引发编译时间错误. 边缘类型参数可用于方法以及类和界面。 Java Generics 也支持多个界限,即 <T 扩展 A & B & C>。 在这种情况下,A 可以是一个界面或类别。
7. Java 通用和遗传
我们知道 Java 继承允许我们将变量 A 分配给另一个变量 B,如果 A 是 B 的子类别,所以我们可能会认为任何通用类型 A 都可以分配给通用类型 B,但不是这样。
1package com.journaldev.generics;
2
3public class GenericsInheritance {
4
5 public static void main(String[] args) {
6 String str = "abc";
7 Object obj = new Object();
8 obj=str; // works because String is-a Object, inheritance in java
9
10 MyClass<String> myClass1 = new MyClass<String>();
11 MyClass<Object> myClass2 = new MyClass<Object>();
12 //myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>
13 obj = myClass1; // MyClass<T> parent is Object
14 }
15
16 public static class MyClass<T>{}
17
18}
我们不允许将 MyClass
8. Java 通用类和子类型
一个类或界面的类型参数和另一个类型参数之间的关系由扩展和执行条款决定.例如,ArrayList
1interface MyList<E,T> extends List<E>{
2}
列表<字符串>的子类型可以是MyList<字符串、Object>、MyList<字符串、Integer>等等。
关键词:Java Generics Wildcards
问题标记 (?)是通用药物中的野卡,代表了一种未知类型。野卡可以用作参数、字段或本地变量类型,有时也可以用作返回类型。
Java Generics Upper Bounded Wildcard 版
假设我们想写一个方法,将返回列表中的数字的总和,所以我们的实现将是这样的东西。
1public static double sum(List<Number> list){
2 double sum = 0;
3 for(Number n : list){
4 sum += n.doubleValue();
5 }
6 return sum;
7 }
现在与上述实现的问题是,它不会与Integers列表或Doubles工作,因为我们知道List
1package com.journaldev.generics;
2
3import java.util.ArrayList;
4import java.util.List;
5
6public class GenericsWildcards {
7
8 public static void main(String[] args) {
9 List<Integer> ints = new ArrayList<>();
10 ints.add(3); ints.add(5); ints.add(10);
11 double sum = sum(ints);
12 System.out.println("Sum of ints="+sum);
13 }
14
15 public static double sum(List<? extends Number> list){
16 double sum = 0;
17 for(Number n : list){
18 sum += n.doubleValue();
19 }
20 return sum;
21 }
22}
它类似于在界面上写我们的代码,在上面的方法中我们可以使用上限类数的所有方法. 请注意,在上限列表中,我们不允许将任何对象添加到列表中,除了null。
Java Generics Unbounded 无限的 Wildcard
有时我们有一个情况,我们希望我们的通用方法与所有类型工作,在这种情况下,可以使用无限的野卡。
1public static void printData(List<?> list){
2 for(Object obj : list){
3 System.out.print(obj + "::");
4 }
5 }
我们可以将列表<字符串>或列表<整数>或任何其他类型的对象列表参数提供到 printData 方法中。
9.3 Java Generics 低限性 Wildcard
假设我们希望在一种方法中将整数添加到整数列表中,我们可以将参数类别保留为List
1public static void addIntegers(List<? super Integer> list){
2 list.add(new Integer(50));
3 }
使用 Generics Wildcard 的分类
1List<? extends Integer> intList = new ArrayList<>();
2List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
关键词:Java Generics Type Erasure
Java 中的 Generics 被添加为在编译时提供类型检查,并且在运行时没有用处,所以 java 编译器使用 type erasure 功能来删除所有通用类型检查代码的字节代码,并在必要时插入类型 casting。
1public class Test<T extends Comparable<T>> {
2
3 private T data;
4 private Test<T> next;
5
6 public Test(T d, Test<T> n) {
7 this.data = d;
8 this.next = n;
9 }
10
11 public T getData() { return this.data; }
12}
Java 编译器将边缘类型参数 T 替换为第一个边缘接口,比较,如下所示:
1public class Test {
2
3 private Comparable data;
4 private Test next;
5
6 public Node(Comparable d, Test n) {
7 this.data = d;
8 this.next = n;
9 }
10
11 public Comparable getData() { return data; }
12}
二、一般性FAQ
12.1)为什么我们在Java中使用 Generics?
通用药物提供了强大的编译时间类型检查,并减少了ClassCastException和对象的明确铸造的风险。
12.2 什么是 T 在 通用药物?
我们使用
12.3 如何在 Java 中使用 Generics?
通用代码确保类型安全. 编译器使用类型删除在编译时删除所有类型参数,以减少运行时的过载。
13. Java 中的通用知识 - 进一步阅读
- 一般不支持分类,所以
List<Number> 数字 = 新的 ArrayList<Integer>();
不会编译,学习 为什么一般不支持分类。 - 我们不能创建通用数组,所以
List<Integer>[] array = 新的 ArrayList<Integer>[10]
不会编译,阅读 为什么我们不能创建通用数组?。
对于Java中的通用药物来说,Java通用药物是一个非常广泛的主题,需要大量的时间来理解和有效地使用它,这篇文章是试图提供通用药物的基本细节,以及我们如何使用它来扩展我们的程序。