泛型
泛型
1.泛型类
泛型指的是在类定义的时候并不会设置类中的属性或方法中参数的具体类型,而是在类使用时再定义。
eg:泛型类的基本语法
class MyClass<T> {
T value1;
}
尖括号 <> 中的 T 被称作是类型参数,用于指代任何类型。实际上这个T你可以任意写,但出于规范的目的,Java 还 是建议我们用单个大写字母来代表类型参数。常见的如:
- T 代表一般的任何类。
- E 代表 Element 的意思,或者 Exception 异常的意思。
- K 代表 Key 的意思 V 代表 Value 的意思,通常与 K 一起配合使用。
- S 代表 Subtype 的意思,文章后面部分会讲解示意。
eg:使用泛型类
MyClass<String> myClass1 = new MyClass<String>();
MyClass<Integer> myClass2 = new MyClass<Integer>();
注意:泛型只能接受类,所有的基本数据类型必须使用包装类!
eg:泛型引入多个类型参数以及使用
/**
*泛型类的基本使用
* @param <T>
* @param <E>
*/
class Point<T,E> {
private T x;
private E y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public E getY() {
return y;
}
public void setY(E y) {
this.y = y;
}
}
public class Test_11_5 {
public static void main(String[] args) {
Point<String,Integer> p = new Point();
p.setX("东经30度");
p.setY(10);
String x = p.getX();//避免了向下转型
int y = p.getY();
System.out.println("x="+x+" "+"y="+y);
}
}
泛型的出现彻底改变了向下转型的需求。引入泛型后,如果明确设置了类型,则为设置类型;
如果没有设置类型, 则默认为Object类型。
2.泛型方法
eg:泛型方法定义
public <T> void MyMethod(T t) {
System.out.println(t);
}
当泛型类与泛型方法共存时,泛型类中的类型参数与泛型方法中的类型参数没有关系
,泛型方法始终以自己定义的类型参数为准。
规范:泛型方法类型参数与泛型类的类型参数不要同名
eg:泛型类与泛型方法共存
/**
* 泛型类与泛型方法共存
* @param <T>
*/
class MyClass<T> {
//泛型类中的普通方法
public void test1(T t) {
System.out.println(t);
}
//泛型类中的泛型方法
public <E> E test2(E e) {
return e;
}
}
public class Test_11_5 {
public static void main(String[] args) {
MyClass<Integer> m = new MyClass<Integer>();
m.test1(2);
System.out.println(m.test2("hello"));
}
}
3.通配符
观察下面的代码:
class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test_11_5 {
public static void main(String[] args) {
Message<Integer> m = new Message<Integer>();
m.setMessage(2);
fun(m);//会出现错误,只能接收String
}
public static void fun(Message<String > t) {
System.out.println(t.getMessage());
}
}
我们需要的解决方案:可以接收所有的泛型类型,但是又不能够让用户随意修改。这种情况就需要使用通配符"?"来 处理
3.1 ? 可以接收任意类型
用于方法中,表示参数可以接收任意类型的泛型类
只能取得类中数据,不能修改数据,因为类型不确定,无法设置确定类型
class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test_11_5 {
public static void main(String[] args) {
Message<Integer> m = new Message<Integer>();
m.setMessage(2);
fun(m);
}
// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
public static void fun(Message<?> t) {
t.setMessage(3);//会出错,无法修改
System.out.println(t.getMessage());
}
}
3.2 ?extends 类 :设置/取得泛型上限
eg:? extends Number:表示泛型必须是Number及其子类
用在类上 T extends 类:T必须为类或者类的子类
用在方法上 ? extends 类:只能接收类或者子类的泛型类
只能取得类中属性值,不能修改值(发生父类到子类的向下转型,
需要强转,由于具体子类不确定,因此无法转型)
eg:观察泛型上限
class Message<T extends Number> {//设置泛型上限
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test_11_5 {
public static void main(String[] args) {
Message<Double> m = new Message<Double>();
m.setMessage(2.2);
fun(m);
}
public static void fun(Message<? extends Number> t) {
t.setMessage(3.3);//具体子类不确定,仍然无法修改
System.out.println(t.getMessage());
}
}
3.3 ? super 类:取得泛型下限-只能用于方法中
eg:? super String :表示此方法只能取得String以及其父类Object
可以设置属性值(子类到父类是自动的向上转型,可以设置值)
eg:设置泛型下限
class Message<T> {
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class Test_11_5 {
public static void main(String[] args) {
Message<String> m = new Message<String>();
m.setMessage("hello");
fun(m);
}
public static void fun(Message<? super String> t) {
t.setMessage("world");//此时可以修改!
System.out.println(t.getMessage());
}
}
4.泛型接口
泛型除了可以定义在类中,也可以定义在接口里面,这种情况我们称之为泛型接口。
//定义一个泛型接口:
interface IInterface<T> {
T test(T t);
}
//对于这个接口的实现子类有两种做法:
//1.子类继续保留泛型
class InterfaceImpl<T> implements IInterface<T> {
}
//2.子类定义时确定好类型
class InterfaceImpl implements IInterface<String> {
}
5.类型擦除(语法糖)
语法糖(方便程序员的开发):仅存在于源码阶段,编译后就消失不见。(泛型、自动拆装箱)
泛型信息仅存在于代码源码编译阶段,进入JVM之前,与泛型相关的信息会被擦除掉,
专业术语:类型擦除
换句话说,泛型类与普通类在java虚拟机内没有任何区别
泛型类进入JVM之前会进行类型擦除,之前泛型类的类型参数若没有指定上限,
会被擦除成为Object类型,如果指定上限,则类型参数被替换为相应类型上限。