泛型
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数类型。
泛型类
定义泛型类在"类名"后添加一对<>,里面定义"泛型名称";
格式:如ArrayList集合
class ArrayList< E > {
public boolean add(E e) { }
public E get(int index) { }
}
使用泛型类:创建对象时,确定泛型的类型
例如:ArrayList< String > list = new ArrayList< String >(); 此时相当于将E的类型替换为String类型。
例如:ArrayList< Integer > list = new ArrayList< Integer >(); 此时相当于将E的类型替换为Integer类型。
/*
泛型类定义格式
格式:修饰符 class 类名<类型> {}
范例:public class Generic<T>{}
此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
*/
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setName("张三");
System.out.println(s.getName());
Teacher t = new Teacher();
t.setAge(30);
// t.setAge("30");
System.out.println(t.getAge());
System.out.println("--------------");
Generic<String>g1=new Generic<String>();
g1.setT("张三");
System.out.println(g1.getT());
Generic<Integer>g2=new Generic<Integer>();
g2.setT(18);
System.out.println(g2.getT());
}
}
class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Teacher {
private Integer age;
public Teacher() {
}
public Teacher(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
class Generic<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
结果:
张三
30
--------------
赵四
18
泛型方法
方法格式:
/**
* <T> 声明此方法持有一个类型T,也可以理解此方法为泛型方法
* T 返回值类型(入参类型是什么类型此返回值就是什么类型)
* @param t1 泛型参数
* @param t2 泛型参数
*/
public static <T> T m1(T t1) {
return t1;
}
详细介绍
/**
* 泛型方法的详细介绍
* @param value 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1. public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2. 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3. <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4. 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <T> T genericMethod(T value)throws InstantiationException ,
IllegalAccessException{
return value;
}
示例:
/*
泛型方法定义格式:
格式:修饰符 <类型> 返回值类型 方法名(类型 变量名){}
范例:public <T> void show(T t){}
*/
public class Test {
public static void main(String[] args) {
// Generic g = new Generic();
// g.show("张三");
// g.show(18);
// g.show(true);
// g.show(13.14);
// Generic<String> g1 = new Generic<String>();
// g1.show("赵四");
// Generic<Integer> g2 = new Generic<Integer>();
// g2.show(18);
// Generic<Boolean> g3 = new Generic<Boolean>();
// g3.show(true);
Generic g=new Generic();
g.show("张三");//String
g.show(18);//int
g.show(true);//boolean
g.show(13.14);//double
}
}
//class Generic {
// public void show(String s) {
// System.out.println(s);
// }
//
// public void show(Integer i) {
// System.out.println(i);
// }
//
// public void show(Boolean b) {
// System.out.println(b);
// }
//}
//泛型类改进
//class Generic<T> {
// public void show(T t) {
// System.out.println(t);
// }
//}
//泛型方法改进
class Generic {
public <T> void show(T t) {
System.out.println(t);
}
}
结果:
张三
18
true
13.14
泛型接口
接口格式
泛型不仅可以声明泛型类,也可以声明泛型接口,声明泛型接口和声明泛型类的语法相似,也是在接口名称后面加< T >
格式:权限修饰符 interface 接口名<泛型标示符>{}
/*
泛型接口定义格式:
格式:修饰符 interface 接口名<类型>{}
范例:public interface Generic<T>{}
*/
public class Test {
public static void main(String[] args) {
Generic<String> g1 = new GenericTmpl<String>();
g1.show("张三");
Generic<Integer> g2 = new GenericTmpl<Integer>();
g2.show(18);
Generic<Boolean> g3 = new GenericTmpl<Boolean>();
g3.show(true);
}
}
interface Generic<T> {
public void show(T t);
}
class GenericTmpl<T> implements Generic<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
泛型接口实现方式
方式一:
子类继续定义泛型类型,通过使用者来确定具体类型
然后使用泛型接口
public class Test {
public static void main(String[] args) {
Fan<String> i = null; // 声明接口对象
i = new Zi<String>("李四"); // 通过子类实例化对象
System.out.println("内容:" + i.getVal());
}
}
interface Fan<T> { // 在接口上定义泛型
public T getVal(); // 定义抽象方法,抽象方法的返回值就是泛型类型
}
//子类继续定义泛型类型,有使用者来确定
class Zi<T> implements Fan<T> { // 定义泛型接口的子类
private T val; // 定义属性
public Zi(T val) { // 通过构造方法设置属性内容
this.setVal(val);
}
public void setVal(T val) {//通过方法参数确定
this.val = val;
}
public T getVal() {
return this.val;
}
}
方式二:
子类确定泛型类型
interface Info<T> { // 在接口上定义泛型
public T getVar(); // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl implements Info<String> { // 子类确定泛型类型
@Override
public String getVar() {//因为在类里面已经确定了泛型类型,所以约束了方法的泛型
return null;
}
}
泛型通配符
类型通配符一般是使用?代替具体的类型参数
例如 List<?> 在逻辑上是List< String >,List< Integer > 等所有List<具体类型实参>的类
通配符的使用
public void show( List < ? > list ){ }:表示可以接收具有"任何泛型"的"List类型"的集合
泛型上限指定
< ? extends E >:只能指向具有E,或者其子类的泛型的对象
public void show( List < ? extends Student > list ){ }:表示只能接收具有"Student"或者"其子类"泛型的集合对象;
泛型下限指定
< ? super E >:只能指向具有E,或者其父类的泛型的对象
还可以看到
public void show( List < ? super Student > stuList ){ } :表示只能接受Student类和其父类的集合对象
public class Test {
public static void main(String[] args) {
List<Student> students = new ArrayList<Student>();
List<Teacher> teachers = new ArrayList<Teacher>();
List<Person> persons = new ArrayList<Person>();
List<String> strings = new ArrayList<String>();
// 可以调用
m1(strings);
m1(strings);
// 因为m2()方法中的参数已经限定了参数泛型上限为Person,
// String类型不在这个范围之内,所以会报错
// m2(strings);
// 类型通配符下限通过形如 List<? super Student>来定义,
// 表示类型只能接受Student及其三层父类类型,如Objec类型的实例。
//m3(teachers);
m3(persons);// 可以通过
}
public static void m1(List<?> data) {
}
public static void m2(List<? extends Person> data) {
}
public static void m3(List<? super Student> stuList) {
}
class Person {
}
class Student extends Person {
}
class Teacher extends Person {
}
}
泛型好处
可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。
1.类型安全
2.消除强制类型转换