黄江仿做网站,爱做网站网址,前端网页设计流程,首页八度空间泛型是Java编程的核心功能之一#xff0c;它是Java 5中引入的。如果您使用的是Java Collections #xff0c;并且版本5或更高版本#xff0c;则可以肯定使用了它。 在集合类中使用泛型非常容易#xff0c;但是它提供了比仅创建集合类型更多的功能#xff0c;我们将在本文中… 泛型是Java编程的核心功能之一它是Java 5中引入的。如果您使用的是Java Collections 并且版本5或更高版本则可以肯定使用了它。 在集合类中使用泛型非常容易但是它提供了比仅创建集合类型更多的功能我们将在本文中尝试学习泛型的功能。 如果我们使用专业术语对泛型的理解有时会变得混乱因此我将尽量保持其简单易懂。 在本教程中我们将研究泛型的以下主题。 Java泛型示例 具有类和接口的泛型 泛型类型命名约定 方法和构造函数中的泛型 泛型有界类型参数 泛型与继承 通用类和子类型 通用通配符 泛型上界通配符 泛型无界通配符 泛型下界通配符 使用泛型通配符进行子类型化 类型擦除 Java泛型示例 Java 5中添加了泛型以提供编译时类型检查并消除使用集合类时常见的ClassCastException风险。 整个收集框架都进行了重写以使用泛型进行类型安全。 让我们看看泛型如何帮助我们安全地使用集合类。 List list new ArrayList();
list.add(abc);
list.add(new Integer(5)); //OKfor(Object obj : list){String str(String) obj; //type casting leading to ClassCastException at runtime
} 上面的代码可以很好地编译但是在运行时会抛出ClassCastException因为我们试图将列表中的Object强制转换为String而其中一个元素是Integer类型。 在Java 5之后我们使用如下收集类。 ListString list1 new ArrayListString(); // java 7 ? ListString list1 new ArrayList();
list1.add(abc);
//list1.add(new Integer(5)); //compiler errorfor(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 GenericsTypeT {private T t;public T get(){return this.t;}public void set(T t1){this.tt1;}public static void main(String args[]){GenericsTypeString type new GenericsType();type.set(Pankaj); //validGenericsType type1 new GenericsType(); //raw typetype1.set(Pankaj); //validtype1.set(10); //valid and autoboxing support}
} 注意main方法中GenericsType类的使用。 我们不需要进行类型转换并且可以在运行时删除ClassCastException。 如果我们在创建时未提供类型则编译器将发出警告“ GenericsType是原始类型。 泛型类型GenericsType T的引用应参数化”。 当我们不提供类型时该类型将成为Object 因此它同时允许String和Integer对象但是我们应始终尝试避免这种情况因为在处理会产生运行时错误的原始类型时我们必须使用类型转换。 提示 我们可以使用SuppressWarnings(rawtypes)注释来抑制编译器警告请参阅Java注释教程 。 还要注意它支持Java自动装箱 。 可比接口是接口中泛型的一个很好的例子它写为 package java.lang;
import java.util.*;public interface ComparableT {public int compareTo(T o);
} 以类似的方式我们可以在接口和类中使用泛型。 我们也可以像Map界面一样具有多个类型参数。 同样我们也可以为参数化类型提供参数化值例如new HashMapString, ListString(); 已验证。 泛型类型命名约定 命名约定可以帮助我们轻松地理解代码拥有命名约定是Java编程语言的最佳实践之一。 因此泛型也带有它自己的命名约定。 通常类型参数名称是单个大写字母以使其易于与Java变量区分开。 最常用的类型参数名称为 E –元素由Java Collections Framework广泛使用例如ArrayListSet等 K –键在地图中使用 N –数字 T –类型 V –值在地图中使用 SUV等–第二第三第四类型 方法和构造函数中的泛型 有时我们不希望整个类都被参数化在这种情况下我们也可以在方法中使用泛型类型。 由于构造函数是一种特殊的方法因此我们也可以在构造函数中使用泛型类型。 这是一个类显示方法中的泛型类型的示例。 package com.journaldev.generics;public class GenericsMethods {//Generics in methodpublic static T boolean isEqual(GenericsTypeT g1, GenericsTypeT g2){return g1.get().equals(g2.get());}public static void main(String args[]){GenericsTypeString g1 new GenericsType();g1.set(Pankaj);GenericsTypeString g2 new GenericsType();g2.set(Pankaj);boolean isEqual GenericsMethods.StringisEqual(g1, g2);//above statement can be written simply asisEqual 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 T extends ComparableT int compare(T t1, T t2){return t1.compareTo(t2);} 这些方法的调用与无界方法类似不同之处在于如果我们尝试使用任何非Comparable的类则会引发编译时错误。 绑定类型参数可以与方法以及类和接口一起使用。 泛型也支持多个范围即T扩展ABC。 在这种情况下A可以是接口或类。 如果A是类则B和C应该是接口。 在多个范围内我们不能有多个类。 泛型与继承 我们知道如果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();objstr; // works because String is-a Object, inheritance in javaMyClassString myClass1 new MyClassString();MyClassObject myClass2 new MyClassObject();//myClass2myClass1; // compilation error since MyClassString is not a MyClassObjectobj myClass1; // MyClassT parent is Object}public static class MyClassT{}} 我们不允许将MyClass String变量分配给MyClass Object变量因为它们不相关实际上MyClass T的父对象是Object。 通用类和子类型 我们可以通过扩展或实现来泛型一个通用类或接口。 一个类或接口的类型参数与另一类或接口的类型参数之间的关系由extend和Implements子句确定。 例如ArrayList E实现了扩展Collection E的List E因此ArrayList String是List String的子类型而List String是Collection String的子类型。 只要不更改type参数子类型关系就会保留下面显示了多个type参数的示例。 interface MyListE,T extends ListE{
} List String的子类型可以是MyList StringObjectMyList StringInteger等。 通用通配符 问号是泛型中的通配符表示未知类型。 通配符可以用作参数字段或局部变量的类型有时还可以用作返回类型。 在调用通用方法或实例化通用类时不能使用通配符。 在以下各节中我们将学习上界通配符下界通配符和通配符捕获。 泛型上界通配符 上限通配符用于在方法中放宽对变量类型的限制。 假设我们要编写一个将返回列表中数字总和的方法那么我们的实现将是这样的。 public static double sum(ListNumber list){double sum 0;for(Number n : list){sum n.doubleValue();}return sum;} 现在上述实现的问题在于它不适用于List of Integers或Doubles因为我们知道List Integer和List Double不相关这在使用上限通配符很有用时。 我们将泛型通配符与extends关键字和上限类或接口一起使用这将允许我们传递上限或其子类类型的参数。 可以像下面的程序一样修改上面的实现。 package com.journaldev.generics;import java.util.ArrayList;
import java.util.List;public class GenericsWildcards {public static void main(String[] args) {ListInteger ints new ArrayList();ints.add(3); ints.add(5); ints.add(10);double sum sum(ints);System.out.println(Sum of intssum);}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 String或List Integer或任何其他类型的Object列表参数。 与上限列表类似我们不允许在列表中添加任何内容。 泛型下界通配符 假设我们要在方法中将整数添加到整数列表中我们可以将参数类型保持为List Integer但它将与Integers捆绑在一起而List Number和List Object也可以容纳整数因此我们可以使用下限通配符来实现此目的。 我们使用带有super关键字和下限类的泛型通配符来实现此目的。 在这种情况下我们可以传递下界或下界的任何超级类型作为参数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 TestT extends ComparableT {private T data;private TestT next;public Test(T d, TestT 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中的泛型泛型是一个非常广泛的话题需要大量时间才能有效地理解和使用它。 本文旨在提供泛型的基本细节以及如何使用泛型来扩展程序的类型安全性。 参考 Java泛型教程–示例类接口方法通配符以及我们的JCG合作伙伴 Pankaj Kumar在Developer Recipes博客上的更多内容。 翻译自: https://www.javacodegeeks.com/2013/07/java-generics-tutorial-example-class-interface-methods-wildcards-and-much-more.html