Java Genrics 是 Java 5 中引入的最重要的功能之一。

如果您一直在使用Java Collections并使用版本 5 或更高版本,那么我确定您已经使用过它。

Java 中具有集合类的泛型非常容易,但是它提供了比仅创建集合类型更多的功能。

我们将在本文中尝试学习泛型的功能。如果我们使用专业术语,对泛型的理解有时会变得混乱,因此,我将尽量保持其简单易懂。

1. Java 中的泛型

Java 5 中添加了泛型,以提供编译时类型检查,并消除了ClassCastException使用集合类时常见的风险。整个收集框架都进行了重写,以使用泛型进行类型安全。让我们看看泛型如何帮助我们安全地使用集合类。

List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); for(Object obj : list){ String str=(String) obj;
}

上面的代码可以很好地编译,但是在运行时会引发ClassCastException,因为我们试图将列表中的对象强制转换为String,而其中一个元素是Integer类型。在Java 5之后,我们使用如下收集类。

List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>();
list1.add("abc");
//list1.add(new Integer(5)); //编译错误 for(String str : list1){
//no type casting needed, avoids ClassCastException
}

请注意,在创建列表时,我们已指定列表中元素的类型为String。因此,如果我们尝试在列表中添加任何其他类型的对象,则该程序将引发编译时错误。还要注意,在循环中中,我们不需要列表中元素的类型转换,因此在运行时删除了ClassCastException。

  1. Java通用类

我们可以使用泛型类型定义自己的类。泛型类型是通过类型进行参数化的类或接口。我们使用尖括号(<>)来指定类型参数。

为了了解其好处,我们假设有一个简单的类:


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。现在,我们将使用Java通用类替换如下所示的相同类。


package com.journaldev.generics; public class GenericsType<T> { private T t; public T get(){
return this.t;
} public void set(T t1){
this.t=t1;
} public static void main(String args[]){
GenericsType<String> 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对象。但是,我们应始终尝试避免这种情况,因为在处理可能产生运行时错误的原始类型时,我们必须使用类型转换。

还要注意,它支持Java自动装箱。

3. Java通用接口

Comparable接口是接口中泛型的一个很好的例子,它写为:


package java.lang;
import java.util.*; public interface Comparable<T> {
public int compareTo(T o);
}

以类似的方式,我们可以在Java中创建通用接口。我们也可以像Map界面具有多个类型参数。同样,我们也可以为参数化类型提供参数化值,例如new HashMap<String, List<String>>();有效。

  1. Java通用类型

Java通用类型命名约定可以帮助我们轻松理解代码,并且具有命名约定是Java编程语言的最佳实践之一。因此,泛型也带有自己的命名约定。通常,类型参数名称是单个大写字母,以可以实现与Java变量区分开。最常用的类型参数名称为:

  • E –元素由Java Collections Framework广泛使用,例如ArrayList,Set等
  • K –键(在Map中使用)
  • N –数字
  • T –类型
  • V –值(在Map中使用)
  • S,U,V等–第二,第三,第四类型

5. Java通用方法

有时我们不希望整个类都被参数化,在这种情况下,我们可以创建java泛型方法。由于构造函数是一种特殊的方法,因此我们也可以在构造函数中使用泛型类型。

这是一个显示Java泛型方法示例的类。


package com.journaldev.generics; public class GenericsMethods { //Java Generic Method
public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
return g1.get().equals(g2.get());
} public static void main(String args[]){
GenericsType<String> g1 = new GenericsType<>();
g1.set("Pankaj"); GenericsType<String> g2 = new GenericsType<>();
g2.set("Pankaj"); boolean isEqual = GenericsMethods.<String>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编译器足够聪明,可以确定要使用的变量的类型,这种功能称为类型变量

6. Java泛型绑定类型参数

假设我们要限制可以在参数化类型中使用的对象的类型,例如在比较两个对象的方法中,并且我们要确保接受的对象是可比较的。要声明一个有界的类型参数,请列出类型参数的名称,然后列出扩展关键字,再加上其上限,以下下面的方法。

public static <T extends Comparable<T>> int compare(T t1, T t2){
return t1.compareTo(t2);
}

这些方法的调用与无界方法类似,不同之处在于,如果我们尝试使用任何非Comparable的类,则引发编译时错误。

绑定类型参数可以与方法以及类和接口一起使用。

Java泛型也支持多个范围,即<T扩展A&B&C>。在这种情况下,A可以是接口或类。如果A是类,则B和C应该是接口。在多个范围内,我们不能有多个类。

7. Java泛型和继承

我们知道,如果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<String> myClass1 = new MyClass<String>();
MyClass<Object> myClass2 = new MyClass<Object>();
//myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>
obj = myClass1; // MyClass<T> parent is Object
} public static class MyClass<T>{} }

8. Java通用类和子类型

我们可以通过扩展或实现来泛型一个通用类或接口。一个类或接口的类型参数与另一类或接口的类型参数之间的关系由extend和实现子句确定。

例如,ArrayList 实现了扩展Collection 的List ,因此ArrayList 是List 的子类型,而List 是Collection 的子类型。

只要不更改type参数,子类型关系就会保留,下面显示了多个type参数的示例。

interface MyList<E,T> extends List<E>{
}

List 的子类型可以是MyList <String,Object>,MyList <String,Integer>等。

9. Java通用通配符

问号(?)是泛型中的通配符,表示未知类型。通配符可以用作参数,字段或局部变量的类型,有时还可以用作返回类型。在调用通用方法或实例化通用类时,不能使用通配符。在以下各节中,我们将学习上界通配符,下界通配符和通配符捕获。

9.1)Java泛型上界通配符

上限通配符用于在方法中放宽对变量类型的限制。假设我们要编写一个将返回列表中数字总和的方法,那么我们的实现将是这样的。

public static double sum(List<Number> list){
double sum = 0;
for(Number n : list){
sum += n.doubleValue();
}
return sum;
}

现在,上述实现的问题在于它不适用于Integers或Doubles,因为我们知道List 和List 不相关,这在使用高层通配符时很有用。我们将通用通配符与extends关键字和上级类或接口一起使用,这将允许我们传递上级子类类型的参数。

可以像下面的程序一样修改上面的实现。

package com.journaldev.generics;

import java.util.ArrayList;
import java.util.List; public class GenericsWildcards { public static void main(String[] args) {
List<Integer> 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;
}
}

就像按照接口编写代码一样,在上述方法中,我们可以使用上限类号码的所有方法。请注意,对于上界列表,除空之外,我们不允许将任何对象添加到列表中。如果我们尝试在sum方法内将元素添加到列表中,则该程序将无法编译。

9.2)Java泛型无限制通配符

有时,我们希望通用方法适用于所有类型,在这种情况下,可以使用无界通配符。与使用<?extends Object>。

public static void printData(List<?> list){
for(Object obj : list){
System.out.print(obj + "::");
}
}

我们可以为_PrintData_方法提供List 或List 或任何其他类型的Object列表参数。与上限列表类似,我们可以在列表中添加任何内容。

9.3)Java泛型下界通配符

假设我们要在方法中将整体添加到整数列表中,我们可以将参数类型保持为List,但可以与Integers捆绑在一起,而List 和List 也可以容纳整数,因此我们可以使用下限通配符来实现。我们使用超级关键字和下限类的泛型通配符(?)来实现此目的。

我们可以传递下界或下界的任何超类型作为参数,在这种情况下,java编译器允许将下界对象类型添加到列表中。

public static void addIntegers(List<? super Integer> list){
list.add(new Integer(50));
}

10.使用泛型通配符进行子类型化

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>

11. Java泛型类型重构

添加了Java泛型以在编译时提供类型检查,并且在运行时没有使用,因此Java编译器使用类型更改功能删除字节码中的所有泛型类型检查代码,并在必要时插入类型转换。类型定义可确保不会为参数化类型创建新的类;因此,泛型不会产生运行时浪费。

例如,如果我们有如下通用类;


public class Test<T extends Comparable<T>> { private T data;
private Test<T> next; public Test(T d, Test<T> 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; }
}

12.泛型常见问题解答

12.1)为什么我们在Java中使用泛型?

泛型提供了强大的编译时类型检查,并降低了ClassCastException和显式对象转换的风险。

12.2)泛型中的T是什么?

我们使用创建通用类,接口和方法。我们在使用T时将其替换为实际类型。

12.3)泛型如何在Java中工作?

通用代码可确保类型安全。编译器使用类型预先在编译时删除所有类型参数,以减少运行时的重载。

13. Java泛型–进一步阅读

  • 泛型不支持子类型,因此List<Number> numbers = new ArrayList<Integer>();将不进行编译
  • 我们无法创建通用副本,因此List<Integer>[] array = new ArrayList<Integer>[10]无法编译

这是所有的Java泛型,Java泛型是非常庞大的,需要大量的时间来了解和有效地使用它。本文提供了泛型的基本细节,以及如何使用泛型来扩展程序的类型安全性。


“不积跬步,无以至千里”,希望未来的你能:有梦为马 随处可栖!加油,少年!

关注公众号:「Java 知己」,每天更新Java知识哦,期待你的到来!

  • 发送「Group」,与 10 万程序员一起进步。
  • 发送「面试」,领取BATJ面试资料、面试视频攻略。
  • 发送「玩转算法」,领取《玩转算法》系列视频教程。
  • 千万不要发送「1024」...



每日福利

Java 泛型示例 - 泛型方法,类,接口的更多相关文章

  1. Java泛型全解析【接口、类、封装类型】

    目录   1.导读  2.为何需要泛型?   3.泛型的定义格式   3.泛型的好处  4.什么时候使用泛型?   5.泛型的擦除   6.泛型的补偿  7.泛型的应用      7.1[泛型类]   ...

  2. java泛型-自定义泛型方法与类型推断总结

    下面是自定义泛型方法的练习: package com.mari.generic; import java.util.ArrayList; import java.util.Collection; im ...

  3. Java泛型:泛型的定义(类、接口、对象)、使用、继承

    地址   http://blog.csdn.net/lirx_tech/article/details/51570138 1. 设计泛型的初衷: 1) 主要是为了解决Java容器无法记忆元素类型的问题 ...

  4. java内部类、接口、集合框架、泛型、工具类、实现类

    .t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.内部类 1.成员内部类. (1)成员内部类的实例化: 外部类名.内部类 ...

  5. 关于Java泛型实现原理的思考与一般用法示例总结

    面向对象的一个重要目标是对代码重用的支持.支持这个目标的一个重要机制就是泛型机制.在1.5版本之前,java并没有直接支持泛型实现,泛型编程的实现时通过使用继承的一些基本概念来完成的. 这种方式的局限 ...

  6. Java 泛型 泛型方法

    Java 泛型 泛型方法 @author ixenos 泛型方法可以定义在普通类中,也可以定义在泛型类中 类型变量放在修饰符(如public static)后面,返回类型的前面 一个static方法无 ...

  7. (一)关于java泛型的学习总结(泛型方法、泛型擦除)

    目录概要 一.泛型方法 二.利用泛型方法的特性实现代码的简化 三. 关于泛型的擦除 四.无界通配符和原生类型区别 五.转型和警告   泛型 一般的类中的属性或方法的参数,只能使用具体的类型:要么是基本 ...

  8. java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

    JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型.  1.Java泛型  其实Java ...

  9. java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

随机推荐

  1. PHP计算二维数组指定元素的和

    array_sum(array_column($arr, 'num')); //计算二维数组指定元素的和 $arr = [ [ 'id'=>1, 'num'=>3, ], [ 'id'=& ...

  2. c#中的跳转语句

    break:跳出循环,执行循环外的语句:continue:跳出此次循环,进入下一次循环: goto:不建议使用 return:终止它所在的方法的执行,并将控制权返回给调用方法.

  3. 浅谈C++ STL string容器

    浅谈C++ STL string容器 本篇随笔简单讲解一下\(C++STL\)中\(string\)容器的使用方法及技巧. string容器的概念 其实\(string\)并不是\(STL\)的一种容 ...

  4. 小程序-picker组件选择数量

    <!-- detail.wxml --> <view class="picker"> <picker range="{{range}}&qu ...

  5. [译]Vulkan教程(09)窗口表面

    [译]Vulkan教程(09)窗口表面 Since Vulkan is a platform agnostic API, it can not interface directly with the ...

  6. thinkphp5.0学习笔记

    2019-11-11学习笔记 安装TP5.0 a)源代码包下载 在thinkphp官网下载(www.thinkphp.cn)下载 完整版本的TP5.0 b) composer 安装 切换到网站的根目录 ...

  7. 如何使用1行代码让你的C++程序控制台输出彩色log信息

    本文首发于个人博客https://kezunlin.me/post/a201e11b/,欢迎阅读最新内容! colorwheel for colored print and trace for cpp ...

  8. uni-app中onLoad不起作用

    最近开始使用uni-app,坑还是很多的 今天在使用onLoad是发现,页面上的onLoad方法是可以起作用的,但是组件中的onLoad方法并没有起作用 后来经过一番尝试后还是不行,看文档发现uni- ...

  9. 【重拾基础】耐人寻味的CSS属性white-space

    <耐人寻味的CSS属性white-space>,本文说的white-space是一个控制换行和空白处理的CSS属性.我曾经被这个属性烦死,一直没记住,今天决定还是写下来好好琢磨下. 属性值 ...

  10. 【HNOI 2017】礼物

    Problem Description 我的室友最近喜欢上了一个可爱的小女生.马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她.每个手环上各有 \(n\) 个装饰物,并且每个装饰物 ...