文章目录
1.什么是泛型
泛型的本质是参数化类型 ,即给类型指定一个参数,然后在使用时在指定此参数具体的值,那样这个类型就可以使用时确定了,这种参数类型可以用在类,接口和方法中,分别被称为泛型类,泛型接口,泛型方法。
泛型的使用使我们的代码更加具有通用性,不会导致定义了一种类型之后其他的类型都无法使用该代码。
通过泛型可以定义类型安全的数据结构(类型安全),而无须使用实际的数据类型(可扩展)。这能够显著提高性能并得到更高质量的代码(高性能),因为您可以重用数据处理算法,而无须复制类型特定的代码(可重用)。
2.为什么需要泛型
Java中引入泛型最主要的目的是将类型检查 工作提前到编译时期,将类型强转(cast) 工作交给编译器,从而在编译时期就获得类型转换异常以及去掉源码中的类型强转代码
例子:
package com.company;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static void genericTest() {
List arrayList = new ArrayList();
arrayList.add("总有刁民想害朕");
arrayList.add(7);
for (int i = 0; i < arrayList.size(); i++) {
Object item = arrayList.get(i);
if (item instanceof String) {
String str = (String) item;
System.out.println("泛型测试 item = " + str);
}else if (item instanceof Integer)
{
Integer inte = (Integer) item;
System.out.println("泛型测试 item = " + inte);
}
}
}
public static void main(String[] args) {
genericTest();
}
}
上述代码中没有泛型之前类型的检查和类型的强转都必须由我们程序员自己负责,如果出错编译器检查不来
使用泛型:
package com.company;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static void genericTest() {
List arrayList = new ArrayList();
arrayList.add("总有刁民想害朕");
arrayList.add(7);
for (int i = 0; i < arrayList.size(); i++) {
Object item = arrayList.get(i);
if (item instanceof String) {
String str = (String) item;
System.out.println("泛型测试 item = " + str);
}else if (item instanceof Integer)
{
Integer inte = (Integer) item;
System.out.println("泛型测试 item = " + inte);
}
}
}
private static void genericTest2() {
List<String> arrayList = new ArrayList<>();
arrayList.add("总有刁民想害朕");
arrayList.add(7); //..(参数不匹配:int 无法转换为String),这里会报错
.
}
public static void main(String[] args) {
genericTest();
}
}
3.泛型作用的对象
泛型有三种使用方式:
泛型类
泛型接口
泛型方法
3.1 泛型类
在类的申明时指定参数,即构成了泛型类,例如下面代码中就指定T
为类型参数,那么在这个类里面就可以使用这个类型了。例如申明T
类型的变量name
,申明T
类型的形参param
等操作。
使用类时就可以传入相应的类型,构建不同类型的实例
package com.company;
import java.util.ArrayList;
import java.util.List;
public class Main {
public class Generic<T> {
public T name;
public Generic(T param){
name=param;
}
public T m(){
return name;
}
}
public static void main(String[] args) {
Main main = new Main();
Generic<String> stringGeneric1 = main.new Generic<>("dfs");
Generic<Integer> stringGeneric2 = main.new Generic<>(111);
Generic<Boolean> stringGeneric3 = main.new Generic<>(true);
System.out.println(stringGeneric1.m());
System.out.println(stringGeneric2.m());
System.out.println(stringGeneric3.m());
}
}
3.2 泛型接口
泛型接口与泛型类的定义基本一致
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Main implements Generator<String>{
String[] names={"ryan","anni","john"};
@Override
public String produce() {
Random random = new Random();
return names[random.nextInt(3)];
}
public static void main(String[] args) {
Main main = new Main();
System.out.println(main.produce());
}
}
interface Generator<T> {
public T produce();
}
3.3 泛型方法
public class Generic<T> {
public T name;
public Generic(){}
public Generic(T param){
name=param;
}
public T m(){
return name;
}
public <E> void m1(E e){ }
public <T> T m2(T e){ }
}
重点看public <E> void m1(E e){ }
这就是一个泛型方法,判断一个方法是否是泛型方法关键看方法返回值前面有没有使用<>
标记的类型,有就是,没有就不是。这个<>
里面的类型参数就相当于为这个方法声明了一个类型,这个类型可以在此方法的作用块内自由使用。 上面代码中,m()
方法不是泛型方法,m1()
与m2()
都是。值得注意的是m2()
方法中声明的类型T
与类申明里面的那个参数T
不是一个,也可以说方法中的T
隐藏了类型中的T
。下面代码中类里面的T
传入的是String
类型,而方法中的T
传入的是Integer
类型。
Generic<String> str=new Generic<>("总有刁民想害朕");
str.m2(123);
4.泛型的使用方法
4.1如何继承一个泛型类
如果不传入具体的类型,则子类也需要指定类型参数,代码如下:
class Son<T> extends Generic<T>{}
如果传入具体参数,则子类不需要指定类型参数
class Son extends Generic<String>{}
4.2 如何实现一个泛型接口
class ImageGenerator<T> implements Generator<T>{
@Override
public T produce() {
return null;
}
}
4.3 如何调用一个泛型方法
和调用普通方法一致,不论是实例方法还是静态方法。
4.4 定义泛型类时声明多个类型
cc<T1,T2>
cc:泛型类名称
cc<Boolean,Float>t=new cc<Boolean,Float>();
4.5 定义泛型类时声明数组类型
package com.company;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Main<T>{
private T[] array;
public void SetT(T[] array){
this.array=array;
}
public T[] getT(){
return this.array;
}
public static void main(String[] args) {
Main<String> stringMain = new Main<>();
String s[]={"c1","c2","c3"};
stringMain.SetT(s);
for (int i = 0; i < stringMain.getT().length; i++) {
System.out.println(stringMain.getT()[i]);
}
}
}
可以在使用泛型机制时声明一个数组,但是不可以使用泛型来建立数组的实例
public class Main<T>{
//private T[] array=new T[10];//不能使用泛型来建立数组的实例
}
5.泛型高级应用
5.1 限制泛型可用类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中队泛型类实例的类型做了限制:
class 类名称<T extends anyClass>
其中,anyClass指某个接口或类
使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类,无论anyClass是接口还是类,在进行泛型限制时都必须使用extends关键字
package com.company;
import java.util.*;
public class Main<T extends List>{
public static void main(String[] args) {
Main<ArrayList> arrayListMain = new Main<>();
Main<LinkedList> linkedListMain = new Main<>();
//这个是错误的,因为HashMap没有实现List()接口
//Main<HashMap> hashMapMain = new Main<HashMap>();
}
}
当没有使用extends关键字限制泛型类型时,默认Object类下的所有子类都可以实例化泛型类对象
下列俩个是等价的
public class a<T>{
}
public class a<T extends Object>{
}
5.2 使用类型通配符
?代表任意类型,例如有如下函数:
public void m3(List<?>list){
for (Object o : list) {
System.out.println(o);
}
}
其参数类型是?
,那么我们调用的时候就可以传入任意类型的List
,如下
str.m3(Arrays.asList(1,2,3));
str.m3(Arrays.asList("总有刁民","想害","朕"));
单独一个?
意义不大,从集合中获取到的对象的类型是Object
类型的,也就只有那几个默认方法可调用,几乎没什么用。如果你想要使用传入的类型那就需要强制类型转换,这是我们接受不了的,不然使用泛型干毛。其真正强大之处是可以通过设置其上下限达到类型的灵活使用。
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时限制这个泛型类的类型实现或继承某个接口或类的子类,要声明这样一个对象可以使用”?“通配符来表示,同时使用extends关键字来对泛型加以限制
使用泛型类型通配符的语法如下:
泛型类名称<? extends List>a=null
其中,<? extends List>表示类型未知,当需要使用泛型对象时,可以单独实例化
5.2.1 通配符上界
通配符上界使用<? extends T>
的格式,意思是需要一个T类型或者T类型的子类,一般T类型都是一个具体的类型,例如下面的代码。
package com.company;
import java.util.*;
public class Main{
public static void printIntValue(List<? extends Number> list) {
for (Number number : list) {
System.out.print(number.intValue()+" ");
}
}
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(222);
integers.add(313);
printIntValue(integers);
}
}
5.2.2 通配符下界
通配符下界使用<? super T>
的格式,意思是需要一个T类型或者T类型的父类,一般T类型都是一个具体的类型,例如下面的代码。
public void fillNumberList(List<? super Number> list) {
list.add(new Integer(0));
list.add(new Float(1.0));
}
6.泛型在静态方法中的问题
泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数,例如下面的代码编译失败
public class Test<T> {
public static T one; //编译错误
public static T show(T one){ //编译错误
return null;
}
}
因为静态方法和静态变量属于类所有,而泛型类中的泛型参数的实例化是在创建泛型类型对象时指定的,所以如果不创建对象,根本无法确定参数类型。但是静态泛型方法 是可以使用的,泛型方法里面的那个类型和泛型类那个类型完全是两回事。
public static <T>T show(T one){
return null;
}