java泛型方法 通配符
泛型是Java编程的核心功能之一,它是Java 5中引入的。如果您使用的是Java Collections ,并且版本5或更高版本,则可以肯定使用了它。 将泛型与集合类一起使用非常容易,但是它提供了比仅创建集合类型更多的功能,我们将在本文中尝试学习泛型的功能。 如果我们使用专业术语,对泛型的理解有时会变得混乱,因此我将尽量保持其简单易懂。
在本教程中,我们将研究泛型的以下主题。
- Java泛型示例
- 具有类和接口的泛型
- 泛型类型命名约定
- 方法和构造函数中的泛型
- 泛型有界类型参数
- 泛型与继承
- 通用类和子类型
- 通用通配符
- 泛型上界通配符
- 泛型无界通配符
- 泛型下界通配符
- 使用泛型通配符进行子类型化
- 类型擦除
Java泛型示例
Java 5中添加了泛型,以提供编译时类型检查并消除使用集合类时常见的ClassCastException
风险。 整个收集框架都进行了重写,以使用泛型进行类型安全。 让我们看看泛型如何帮助我们安全地使用集合类。
List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); //OK
for(Object obj : list){
String str=(String) obj; //type casting leading to ClassCastException at runtime
}
上面的代码可以很好地编译,但是在运行时抛出ClassCastException,因为我们试图将列表中的Object强制转换为String,而其中一个元素是Integer类型。 在Java 5之后,我们使用如下收集类。
List list1 = new ArrayList(); // java 7 ? List list1 = new ArrayList<>();
list1.add("abc");
//list1.add(new Integer(5)); //compiler error
for(String str : list1){
//no type casting needed, avoids ClassCastException
}
请注意,在创建列表时,我们已指定列表中元素的类型为String。 因此,如果我们尝试在列表中添加任何其他类型的对象,则该程序将引发编译时错误。 还要注意,在for循环中,我们不需要类型转换列表中的元素,因此在运行时删除了ClassCastException。
具有类和接口的泛型
我们可以使用泛型类型定义自己的类和接口。 泛型类型是通过类型进行参数化的类或接口。 我们使用尖括号(<>)来指定type参数。
为了了解其好处,可以说我们有一个简单的类:
package com.journaldev.generics;
public class GenericsTypeOld {
private Object t;
public Object get() {
return t;
}
public void set(Object t) {
this.t = t;
}
public static void main(String args[]){
GenericsTypeOld type = new GenericsTypeOld();
type.set("Pankaj");
String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
}
}
请注意,在使用此类时,我们必须使用类型转换,并且它可以在运行时产生ClassCastException。 现在,我们将使用泛型来重写具有泛型类型的相同类,如下所示。
package com.journaldev.generics;
public class GenericsType {
private T t;
public T get(){
return this.t;
}
public void set(T t1){
this.t=t1;
}
public static void main(String args[]){
GenericsType type = new GenericsType<>();
type.set("Pankaj"); //valid
GenericsType type1 = new GenericsType(); //raw type
type1.set("Pankaj"); //valid
type1.set(10); //valid and autoboxing support
}
}
注意main方法中GenericsType类的使用。 我们不需要进行类型转换,并且可以在运行时删除ClassCastException。 如果我们在创建时未提供类型,则编译器将发出警告,“ GenericsType是原始类型。 泛型类型GenericsType Object
,因此它同时允许String和Integer对象,但是我们应始终尝试避免这种情况,因为在处理会产生运行时错误的原始类型时,我们将必须使用类型转换。
提示 :我们可以使用@SuppressWarnings("rawtypes")
注释来取消编译器警告,请参阅Java注释教程 。
还要注意,它支持Java自动装箱 。
可比接口是接口中泛型的一个很好的例子,它写为:
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
以类似的方式,我们可以在接口和类中使用泛型。 我们也可以像在Map界面中那样具有多个类型参数。 同样,我们也可以为参数化类型提供参数化值,例如new HashMap
已验证。
泛型类型命名约定
命名约定可以帮助我们轻松地理解代码,拥有命名约定是Java编程语言的最佳实践之一。 因此,泛型也带有它自己的命名约定。 通常,类型参数名称是单个大写字母,以使其易于与Java变量区分开。 最常用的类型参数名称为:
- E –元素(由Java Collections Framework广泛使用,例如ArrayList,Set等)
- K –键(在地图中使用)
- N –数字
- T –类型
- V –值(在地图中使用)
- S,U,V等–第二,第三,第四类型
方法和构造函数中的泛型
有时我们不希望对整个类进行参数化,在这种情况下,我们也可以在方法中使用泛型类型。 由于构造函数是一种特殊的方法,因此我们也可以在构造函数中使用泛型类型。
这是一个类,显示方法中的泛型类型的示例。
package com.journaldev.generics;
public class GenericsMethods {
//Generics in method
public static boolean isEqual(GenericsType g1, GenericsType g2){
return g1.get().equals(g2.get());
}
public static void main(String args[]){
GenericsType g1 = new GenericsType<>();
g1.set("Pankaj");
GenericsType g2 = new GenericsType<>();
g2.set("Pankaj");
boolean isEqual = GenericsMethods.isEqual(g1, g2);
//above statement can be written simply as
isEqual = GenericsMethods.isEqual(g1, g2);
//This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
//Compiler will infer the type that is needed
}
}
注意isEqual方法签名显示了在方法中使用泛型类型的语法。 还要注意如何在我们的java程序中使用这些方法。 我们可以在调用这些方法时指定类型,也可以像普通方法一样调用它们。 Java编译器足够聪明,可以确定要使用的变量的类型,这种功能称为类型推断 。
泛型有界类型参数
假设我们想限制可以在参数化类型中使用的对象的类型,例如,在比较两个对象的方法中,并且我们要确保接受的对象是可比较的。 要声明一个有界的类型参数,请列出该类型参数的名称,然后是extends关键字,然后是其上限,类似于下面的方法。
public static > int compare(T t1, T t2){
return t1.compareTo(t2);
}
这些方法的调用与无界方法类似,不同之处在于,如果我们尝试使用任何非Comparable的类,则会引发编译时错误。
绑定类型参数可以与方法以及类和接口一起使用。
泛型也支持多个范围,即
泛型与继承
我们知道,如果A是B的子类,则Java继承允许我们将变量A分配给另一个变量B。因此,我们可能认为可以将A的任何泛型类型分配给B的泛型类型,但事实并非如此。 让我们用一个简单的程序看看。
package com.journaldev.generics;
public class GenericsInheritance {
public static void main(String[] args) {
String str = "abc";
Object obj = new Object();
obj=str; // works because String is-a Object, inheritance in java
MyClass myClass1 = new MyClass();
MyClass
我们不允许将MyClass
通用类和子类型
我们可以通过扩展或实现来泛型一个通用类或接口。 一个类或接口的类型参数与另一类或接口的类型参数之间的关系由extend和Implements子句确定。
例如,ArrayList
只要不更改type参数,子类型关系就会保留,下面显示了多个type参数的示例。
interface MyList extends List{
}
List
通用通配符
问号(?)是泛型中的通配符,表示未知类型。 通配符可以用作参数,字段或局部变量的类型,有时还可以用作返回类型。 在调用通用方法或实例化通用类时,我们不能使用通配符。 在以下各节中,我们将学习上界通配符,下界通配符和通配符捕获。
泛型上界通配符
上限通配符用于放松方法中对变量类型的限制。 假设我们要编写一个将返回列表中数字总和的方法,那么我们的实现将是这样的。
public static double sum(List list){
double sum = 0;
for(Number n : list){
sum += n.doubleValue();
}
return sum;
}
现在,上述实现的问题在于它不适用于List of Integers或Doubles,因为我们知道List
可以像下面的程序一样修改上面的实现。
package com.journaldev.generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsWildcards {
public static void main(String[] args) {
List ints = new ArrayList<>();
ints.add(3); ints.add(5); ints.add(10);
double sum = sum(ints);
System.out.println("Sum of ints="+sum);
}
public static double sum(List extends Number> list){
double sum = 0;
for(Number n : list){
sum += n.doubleValue();
}
return sum;
}
}
就像按照接口编写代码一样,在上面的方法中,我们可以使用上限类Number的所有方法。 请注意,对于上界列表,除null之外,我们不允许将任何对象添加到列表中。 如果我们尝试在sum方法内将元素添加到列表中,则该程序将无法编译。
泛型无界通配符
有时,我们希望通用方法适用于所有类型,在这种情况下,可以使用无界通配符。 与使用<?相同 扩展Object>。
public static void printData(List> list){
for(Object obj : list){
System.out.print(obj + "::");
}
}
我们可以为printData方法提供List
泛型下界通配符
假设我们要在方法中将整数添加到整数列表中,我们可以将参数类型保持为List
在这种情况下,我们可以传递下界或下界的任何超级类型作为参数,java编译器允许将下界对象类型添加到列表中。
public static void addIntegers(List super Integer> list){
list.add(new Integer(50));
}
使用泛型通配符进行子类型化
List extends Integer> intList = new ArrayList<>();
List extends Number> numList = intList; // OK. List extends Integer> is a subtype of List extends Number>
类型擦除
添加了泛型以在编译时提供类型检查,并且在运行时无用,因此Java编译器使用类型擦除功能来删除字节码中的所有泛型类型检查代码,并在必要时插入类型转换。 类型擦除可确保不会为参数化类型创建新的类; 因此,泛型不会产生运行时开销。
例如,如果我们有如下通用类;
public class Test> {
private T data;
private Test next;
public Test(T d, Test n) {
this.data = d;
this.next = n;
}
public T getData() { return this.data; }
}
Java编译器用第一个绑定接口Comparable替换了绑定类型参数T,如下代码:
public class Test {
private Comparable data;
private Test next;
public Node(Comparable d, Test n) {
this.data = d;
this.next = n;
}
public Comparable getData() { return data; }
}
多数民众赞成在Java中的泛型,泛型是一个非常大的话题,需要大量时间才能有效地理解和使用它。 本文旨在提供泛型的基本细节,以及如何使用泛型来扩展程序的类型安全性。
翻译自: https://www.javacodegeeks.com/2013/07/java-generics-tutorial-example-class-interface-methods-wildcards-and-much-more.html
java泛型方法 通配符