Java Generics示例教程 - 通用方法,类,接口
Java Generics示例教程 - 通用方法,类,接口
Java Genrics是Java 5中引入的最重要的功能之一。如果您一直在使用Java Collections 对于版本5或更高版本,我确信您已经使用过它。 带有集合类的Java泛型非常简单,但它提供了许多功能,而不仅仅是创建集合类型,我们将尝试在本文中学习泛型的功能。如果我们使用行话词,理解泛型有时会变得混乱,所以我会尽量保持简单易懂。
Java Generics - Java中的泛型
我们将在java中查看以下泛型主题。
- Java中的泛型
- Java通用类
- Java通用接口
- Java通用类型
- Java通用方法
- Java泛型有界类型参数
- Java泛型和继承
- Java通用类和子类型
- Java Generics通配符
- Java泛型上限有界通配符
- Java Generics无界通配符
- Java Generics下限通配符
- 使用泛型通配符进行子类型化
- Java Generics Type Erasure
Java中的泛型
在Java 5中添加了泛型,以提供编译时类型检查,并消除ClassCastException
在使用集合类时常见的风险。整个收集框架被重新编写,以使用泛型进行类型安全。让我们看看泛型如何帮助我们安全地使用集合类。
List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); //OKfor(Object obj : list){//type casting leading to ClassCastException at runtimeString str=(String) obj;
}
上面的代码编译很好但在运行时抛出ClassCastException,因为我们试图将列表中的Object强制转换为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)); //compiler errorfor(String str : list1){//no type casting needed, avoids ClassCastException
}
请注意,在创建列表时,我们已指定列表中的元素类型为String。因此,如果我们尝试在列表中添加任何其他类型的对象,程序将抛出编译时错误。另请注意,在for循环中,我们不需要在列表中对元素进行类型转换,因此在运行时删除了ClassCastException。
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"); //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自动装箱。
Java通用接口
可比较的接口是接口中泛型的一个很好的例子,它的编写如下:
package java.lang;
import java.util.*;public interface Comparable<T> {public int compareTo(T o);
}
以类似的方式,我们可以在java中创建通用接口。我们也可以在Map接口中有多个类型参数。我们也可以为参数化类型提供参数化值,例如new HashMap<String, List<String>>();
有效。
Java通用类型
Java通用类型命名约定有助于我们轻松理解代码,并且具有命名约定是Java编程语言的最佳实践之一。因此泛型也附带了它自己的命名约定。通常,类型参数名称是单个大写字母,以便于与java变量轻松区分。最常用的类型参数名称是:
- E - Element(Java Collections Framework广泛使用,例如ArrayList,Set等)
- K - 键(用于地图)
- N - 数字
- T型
- V - 值(用于地图)
- S,U,V等 - 第2,第3,第4类型
Java通用方法
有时我们不希望对整个类进行参数化,在这种情况下我们可以创建java泛型方法。由于构造函数是一种特殊的方法,我们也可以在构造函数中使用泛型类型。
这是一个显示java泛型方法示例的类。
package com.journaldev.generics;public class GenericsMethods {//Java Generic Methodpublic 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 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编译器足够聪明,可以确定要使用的变量类型,此工具称为类型推断。
Java泛型有界类型参数
假设我们要限制可在参数化类型中使用的对象类型,例如在比较两个对象的方法中,我们希望确保接受的对象是可比较对象。要声明有界类型参数,请列出类型参数的名称,然后是extends关键字,后跟其上限,类似于下面的方法。
public static <T extends Comparable<T>> int compare(T t1, T t2){return t1.compareTo(t2);}
这些方法的调用类似于无界方法,除非我们尝试使用任何不可比较的类,否则会抛出编译时错误。
有界类型参数可以与方法以及类和接口一起使用。
Java Generics也支持多个边界,即<T扩展A&B&C>。在这种情况下,A可以是接口或类。如果A是类,那么B和C应该是接口。我们不能在多个边界中拥有多个类。
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 javaMyClass<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>{}}
我们不允许将MyClass <String>变量分配给MyClass <Object>变量,因为它们不相关,实际上MyClass <T> parent是Object。
Java通用类和子类型
我们可以通过扩展或实现它来对通用类或接口进行子类型化。一个类或接口的类型参数与另一个类或参数的类型参数之间的关系由extends和implements子句确定。
例如,ArrayList <E>实现扩展Collection <E>的List <E>,因此ArrayList <String>是List <String>的子类型,List <String>是Collection <String>的子类型。
只要我们不更改类型参数,子类型关系就会被保留,下面显示了多个类型参数的示例。
interface MyList<E,T> extends List<E>{
}
List <String>的子类型可以是MyList <String,Object>,MyList <String,Integer>等等。
Java Generics通配符
问号(?)是泛型中的通配符,表示未知类型。通配符可以用作参数,字段或局部变量的类型,有时也可以用作返回类型。在调用泛型方法或实例化泛型类时,我们不能使用通配符。在接下来的部分中,我们将了解上限有界通配符,下限有界通配符和通配符捕获。
Java泛型上限有界通配符
上限有界通配符用于放宽方法中变量类型的限制。假设我们想要编写一个返回列表中数字总和的方法,那么我们的实现将是这样的。
public static double sum(List<Number> list){double sum = 0;for(Number n : list){sum += n.doubleValue();}return sum;}
现在上面实现的问题是它不能与List of Integers或Doubles一起工作,因为我们知道List <Integer>和List <Double>是不相关的,这是上边界通配符有用的时候。我们使用泛型通配符与扩展关键字和上限类或接口,它允许我们通过上限的争论或它的子类的类型。
上述实现可以像下面的程序一样进行修改。
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;}
}
它类似于在接口方面编写我们的代码,在上面的方法中我们可以使用上限类Number的所有方法。请注意,对于上限列表,我们不允许向列表中添加除null之外的任何对象。如果我们尝试在sum方法中的列表中添加一个元素,程序将无法编译。
Java Generics无界通配符
有时我们希望我们的泛型方法适用于所有类型,在这种情况下可以使用无界通配符。与使用<?相同 extends Object>。
public static void printData(List<?> list){for(Object obj : list){System.out.print(obj + "::");}}
我们可以为printData方法提供List <String>或List <Integer>或任何其他类型的Object list参数。与上限列表类似,我们不允许向列表添加任何内容。
Java Generics下限通配符
假设我们要将Integers添加到方法中的整数列表中,我们可以将参数类型保持为List <Integer>,但它将与Integers绑定,而List <Number>和List <Object>也可以包含整数,所以我们可以使用下限通配符来实现这一点。我们使用带有超级关键字和下限类的泛型通配符(?)来实现这一点。
在这种情况下,我们可以传递下限或任何超类型的下限作为参数,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 Generics Type Erasure
添加了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; }
}
Java中的泛型 - 进一步阅读
- 泛型不支持子类型,因此
List<Number> numbers = new ArrayList<Integer>();
不会编译,了解为什么泛型不支持子类型。 - 我们不能创建泛型数组,所以
List<Integer>[] array = new ArrayList<Integer>[10]
不会编译,读取为什么我们不能创建泛型数组?。
这就是所有的在Java泛型,Java泛型是一个非常庞大的题目,需要大量的时间来了解和有效地使用它。这篇文章试图提供泛型的基本细节,以及如何使用它来扩展我们的类型安全程序。
Java Generics示例教程 - 通用方法,类,接口相关推荐
- Java反射示例教程
Java反射示例教程 Java Reflection提供检查和修改应用程序的运行时行为的能力.Java中的反射是核心java的一个先进主题.使用java反射我们可以检查一个类,在运行时接口,枚举,获取 ...
- Java设计模式 - 示例教程
Java设计模式 - 示例教程 设计模式在软件开发人员中非常流行.设计模式是针对常见软件问题的良好描述的解决方案.我已经写了很多关于java设计模式的文章.您可以订阅我们的时事通讯下载PDF电子书(1 ...
- java 泛型应用,通用返回类,泛型方法,泛型静态方法
java 泛型应用,通用返回类,泛型方法,泛型静态方法 泛型简介 应用示例 静态方法增加泛型参数 调用静态公有方法 对比调用非静态公有方法(成员方法) 泛型简介 这里不多说明,详见 java泛型入门 ...
- java 泛型示例_Java泛型示例教程–泛型方法,类,接口
java 泛型示例 Java Genrics is one of the most important features introduced in Java 5. Java Genrics是Java ...
- java枚举类型原理_Java枚举类接口实例原理解析
这篇文章主要介绍了Java枚举类接口实例原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 枚举类可以实现一个或多个接口.与普通类实现接口完全一 ...
- java私有属性和私有方法_Java接口–历年来Java 9之旅–默认方法和私有方法
java私有属性和私有方法 介绍 Java接口已经比Java版本进行了更多的演变,而Java 8和Java 9则有所发展.在本文中,我们将介绍接口在Java 8之前的样子以及如何在Java 8和Jav ...
- java学习(2)方法/类+成员/局部变量+private/this关键字+构造方法+标准类
文章目录 1.方法+类 (1)创建类 (2)对象的在调用时的内存 (3)方法的参数.返回值可以是一个对象 2.成员变量和局部变量 3.private关键字 4.this关键字 5.构造方法 6.定义一 ...
- Java代码示例: 使用reflections工具类获取某接口下所有的实现类
pom依赖 <dependency><groupId>org.reflections</groupId><artifactId>reflections& ...
- java web3j示例教程
最近在用java开发区块链项目,妥妥的菜鸟选手一枚,碰到问题,翻牌各种资源查询,百度,谷歌,GitHub,脑细胞死了不知道多少了,今天,发现了一个非常棒的网站,特地向大家推荐,可以说涵盖了web3j在 ...
最新文章
- 奇安信cdn配置教程_PicGo+jsDelivr+GitHub搭建免费cdn加速的图床
- 三圆相交阴影部分面积_小学六年级图形面积的题很多家长都不会,一些初中生也未必会做...
- 两杯咖啡只卖2.5?瑞幸价格出错被“薅羊毛” 官方道歉了...
- 32. PHP Cookies
- 数据结构算法设计题汇总
- 仗剑走天涯,执手闯天下
- 一文看清Libra全景(超长22000字,慎点)
- USRPx310的底板介绍
- chrome自动代理检测(这玩意并没什么用,还会影响你访问某些网页)
- 强对偶性、弱对偶性以及KKT条件的证明(对偶问题的几何证明)
- spring-mybatis实现注册通过邮箱发送激活码激活注册用户
- SAP ADM100 学习笔记
- LeetCode 643 题解
- R软件--GEO数据分析教程:差异性分析、富集分析(GO\KEGG\DO)
- Windows下使用taskkill 命令结束进程
- 海康威视工业相机SDK二次开发(VS+Opencv+QT+海康SDK+C++)(一)
- 百度副总裁李硕:通过“一企一档”等模式提升企业智能化水平
- Kafka批量消费模式
- DOTA的常用礼仪用语及英文缩写
- Hexo博客配置SSL证书
热门文章
- Magento:Service Temporarily Unavailable ?
- Shell编程中的“局部变量”和“导出变量” (export 用法)
- 设定应用程式上的 Badge -- IOS
- Network Embedding 与 Graph Embedding
- LocalDateTime日期转换错误:JSON parse error: Cannot deserialize value of type java.time.LocalDateTime
- Centos7下python3安装ipython
- mysql 用户授权
- 【转载】ORM的概念
- 为DbHelper工具类添加关闭资源的方法 jdbc 20210412_212728.mp4
- DDL 操作表 创建表