泛型(Generics)
为了能够更好的学习容器,我们首先要先来学习一个概念:泛型。
泛型基本概念,泛型是JDK5.0以后增加的新特性。
泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入。我们可以把“泛型”理解为数据类型的一个占位符(类似:形式参数),即告诉编译器,在调用泛型时必须传入实际类型.
参数化类型,白话说就是:
1.把类型当作是参数一样传递。
2. <数据类型> 只能是引用类型。
泛型的好处
在不使用泛型的情况下,我们可以使用Object类型来实现任意的参数类型,但是在使用时需要我们强制进行类型转换。这就要求程序员明确知道实际类型,不然可能引起类型转换错误;但是,在编译期我们无法识别这种错误,只能在运行期发现这种错误。使用泛型的好处就是可以在编译期就识别出这种错误,有了更好的安全性;同时,所有类型转换由编译器完成,在程序员看来都是自动转换的,提高了代码的可读性。
总结一下,就是使用泛型主要是两个好处:
1、代码可读性更好【不用强制转换】
2、程序更加安全【只要编译时期没有警告,运行时期就不会出现ClassCastException异常】
类型擦除
编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。
泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。
泛型主要是方便了程序员的代码编写,以及更好的安全性检测。
实时效果反馈
1.泛型是在JDK哪个版本中增加的新特性?
A JDK5.0
B JDK6.0
C JDK7.0
D JDK8.0
2.泛型的本质是什么?
A 数据的参数化
B 对象的参数化
C 数据类型的参数化
D 基本类型的参数化
答案
1=>A 2=>C
泛型类
泛型标记
定义泛型时,一般采用几个标记:E、T、K、V、N、?。他们约定
俗称的含义如下:
泛型标记 | 对应单词 | 说明 |
E | Element | 在容器中使用,表示容器中的元素 |
T | Type | 表示普通的JAVA类 |
K | Key | 表示键,例如:Map中的键Key |
V | Value | 表示值 |
N | Number | 表示数值类型 |
? | 表示不确定的JAVA类型 |
泛型类的使用
语法结构
public class 类名<泛型标识符号> { } public class 类名<泛型标识符号,泛型标识符号> { }
示例
public class Generic<T> { private T flag; public void setFlag(T flag){ this.flag = flag; } public T getFlag(){ return this.flag; } }
public class Test { public static void main(String[] args) { //创建对象时,指定泛型具体类型。 Generic<String> generic = new Generic<>(); generic.setFlag("admin"); String flag = generic.getFlag(); System.out.println(flag); //创建对象时,指定泛型具体类型。 Generic<Integer> generic1 = new Generic<>(); generic1.setFlag(100); Integer flag1 = generic1.getFlag(); System.out.println(flag1); } }
实时效果反馈
1.如下哪个选项是正确定义泛型类的语法
A public class <泛型标识符号> 类名
B public <泛型标识符号> class 类名
C <泛型标识符号> public class 类名
D public class 类名<泛型标识符号>
答案
1=>D
泛型接口
泛型接口和泛型类的声明方式一致。
泛型接口的使用
语法结构
public interface 接口名<泛型标识符号> { } public interface 接口名<泛型标识符号,泛型标识符号>{ }
示例
public interface IGeneric<T> { T getName(T name); } //在实现接口时传递具体数据类型 public class IgenericImpl implements Igeneric<String> { @Override public String getName(String name) { return name; } } //在实现接口时仍然使用泛型作为数据类型 public class IGenericImpl2<T> implements IGeneric<T>{ @Override public T getName(T name) { return name; } } public class Test { public static void main(String[] args) { IGeneric<String> igeneric= new IGenericImpl(); String name = igeneric.getName("old"); System.out.println(name); IGeneric<String> igeneric1 = new IGenericImpl2<>(); String name1 = igeneric1.getName("it"); System.out.println(name1); } }
实时效果反馈
1.如下哪个选项是正确定义泛型接口的语法
A public interface<泛型标识符号> 接口名
B public <泛型标识符号> interface 接口名
C <泛型标识符号> public interface 接口名
D public interface 接口名<泛型标识符号>
答案
1=>D
泛型方法
类上定义的泛型,在方法中也可以使用。但是,我们经常需要仅仅在某一个方法上使用泛型,这时候可以使用泛型方法。
调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型
泛型方法的使用
非静态方法
非静态方法可以使用泛型类中所定义的泛型,也可以将泛型定义在方法上。
语法结构
//无返回值方法 public <泛型标识符号> void getName(泛型标识符号name) { } //有返回值方法 public <泛型标识符号> 泛型标识符号 getName(泛型标识符号 name) { }
示例
public class MethodGeneric { public <T> void setName(T name){ System.out.println(name); } public <T> T getAge(T age){ return age; } }
public class Test2 { public static void main(String[] args) { MethodGeneric methodGeneric = new MethodGeneric(); methodGeneric.setName("old"); Integer age = methodGeneric.getAge(123); System.out.println(age); }
静态方法
静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型,所以必须要将泛型定义在方法上。
语法结构
//无返回值静态方法 public static <泛型标识符号> void setName(泛型标识符号 name){ } //有返回值静态方法 public static <泛型标识符号> 泛型表示符号getName(泛型标识符号 name){ }
示例
public class MethodGeneric { public static <T> void setFlag(T flag){ System.out.println(flag); } public static <T> T getFlag(T flag){ return flag; } }
public class Test4 { public static void main(String[] args) { MethodGeneric.setFlag("old"); Integer flag1 = MethodGeneric.getFlag(123123); System.out.println(flag1); } }
实时效果反馈
1.如下哪个选项是正确定义泛型方法的语法
A <泛型标识符号> public void getName(泛型标识符号 name)
B public void <泛型标识符号> getName(泛型标识符号 name)
C public <泛型标识符号> void getName(泛型标识符号 name)
D public void getName <泛型标识符号>(泛型标识符号 name)
答案
1=>C
泛型方法与可变参数
在泛型方法中,泛型也可以定义可变参数类型。
语法结构
public class MethodGeneric { public <T> void method(T...args){ for(T t:args){ System.out.println(t); } } } public class Test5 { public static void main(String[] args) { MethodGeneric methodGeneric = new MethodGeneric(); String[] arr = new String[]{"a","b","c"}; Integer[] arr2 = new Integer[]{1,2,3}; methodGeneric.method(arr); methodGeneric.method(arr2); } }
实时效果反馈
1.如下哪个选项是正确的在可变参数中使用泛型
A public <泛型标识符号> void showMsg(泛型标识符号... agrs)
B public void showMsg(<泛型标识符号>... agrs)
C public <泛型标识符号> void showMsg(<泛型标识符号>... agrs)
D public <泛型标识符号> void showMsg(Object... agrs)
答案
1=>A