在JDK1.5以后,Java引入了泛型的概念,这是一种非常重要的实现技术,它可以帮助我们解决程序的参数转换问题。
泛型的基本语法
class My_Class<T>{
private T value;
//......
}
其中的<T>被称作类型参数,可以用来指代任何类型,这个字母是可以随便写的,但是出于开发使用中规范的目的,我们经常使用单个的大写字母来代表类型参数。
T:代表一般的任何类
E:代表Element,或者代表Exception异常
K:代表Key
V:代表Value的意思,通常与K一起配合使用
所以,如果一个类以<T>的形式定义,那么这个类就是一个泛型类。
使用泛型类的时候需要注意:泛型只能接受类,所以,所有的基本数据类型必须使用包装类。
class My_Class<T,E>{//泛型可以接受多个类型参数
private T value;
private E value2;
//......
}
public class Main{
public static void main(String[] args){
My_Class<String,Integer> my_class1 = new My_Class<String,Integer>();JDK1.5的语法
My_Class<Integer,String> my_class2 = new My_Class<>();//JDK1.7之后的新语法
}
}
我举一个简单的泛型例子
class My_Class<T,E>{
private T age;
private E name;
public void setAge(T age) {
this.age = age;
}
public void setName(E name) {
this.name = name;
}
public T getAge() {
return age;
}
public E getName() {
return name;
}
}
public class Main{
public static void main(String[] args){
My_Class<Integer,String> my_class = new My_Class<>();//相当于把T设置成Integer,E为String
my_class.setAge(20);
my_class.setName("dxy");
Integer age = my_class.getAge();
String name = my_class.getName();
System.out.println("name:"+name+" "+"age:"+age);
}
}
泛型可以避免向下转型,代码中引入了泛型后,如果明确的设置了类型,则为设置类型,否则默认为Object类型。
泛型除了可以用来定义类,也可以定义方法,只不过类型参数要写在返回类型之前。
public <W> void My_Method(W w){
System.out.println(w);
}
方法中的W被称为参数化类型,它不是运行时真正的参数。
另外,如果是声明过的类型参数,还是可以作为返回值类型的。
public <W> W My_Method(W w){
return w;
}
泛型方法和泛型类是可以共存的,但是,我们在设置这两者的类型参数时,需要注意一个问题
class My_Class<T>{
private T age;
public <T> T My_Method(T t){
return t;
}
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return age;
}
}
public class Main{
public static void main(String[] args){
My_Class<Integer> my_class = new My_Class<>();
System.out.println(my_class.My_Method(24));
}
}
在段代码里,类的类型参数和方法的类型参数同名,使用起来很容易出现混淆的情况。所以我们最好在类上在设置一个方法的类型参数,这样就可以避免混淆。
class My_Class<T>{
private T age;
public <E> E My_Method(E e){
return e;
}
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return age;
}
}
public class Main{
public static void main(String[] args){
My_Class<Integer> my_class = new My_Class<>();
System.out.println(my_class.<String>My_Method("dxy"));//调用方法的时候设置方法的类型参数
}
}
通配符
引入泛型之后,会出现新的情况:参数的统一问题
class My_Class<T>{
private T age;
public void setAge(T age) {
this.age = age;
}
public T getAge() {
return age;
}
}
public class Main{
public static void main(String[] args){
My_Class<Integer> my_class = new My_Class<>();
my_class.setAge(20);
My_Method(my_class);
}
public static void My_Method(My_Class<Integer> my_class){
System.out.println(my_class.getAge());
}
}
如果现在My_Method方法接受的泛型类型不是Integer,那么就会出现错误。所以我们需要一种可以接受任意类型的方法。这就是通配符的作用,先看代码。
public static void My_Method(My_Class<?> my_class){
System.out.println(my_class.getAge());
}
这里使用了 ? 通配符,它的意思时可以接收任何类型,不过,因为我们不知道会接到什么样的类型,所以不能再My_Method方法里对类中的属性进行修改。
通配符(? extends 类名称):设置泛型上限
例如 <? exends Number>:表示当前只能接收Number类以及它的子类。
通配符(? super 类名称):设置泛型下限
例如<? super String>:表示只能接收String类以及它的父类object类。
在这里有一点要说明的是:使用泛型上限的时候,既可以用在类型参数上,也可以用在方法参数上。但是由于不确定接收的类型,还是无法对泛型类中的属性进行修改。使用泛型下限的时候,只能用在方法参数上,但是可以对泛型类中的属性进行修改。
泛型接口
泛型除了可以定义在类中与方法中,也可以定义在接口中。
interface My_Interface<T>{//泛型接口
void Method(T t);
}
对于泛型接口实现子类有两种方法:
1:在子类定义时继续使用泛型
interface My_Interface<T>{//泛型接口
void Method(T t);
}
class My_Class<T> implements My_Interface<T>{//子类实现接口,继续使用泛型。不给出具体类型
@Override
public void Method(T t) {
System.out.println(t);
}
}
2:子类在实现接口的时候就给出具体的类型
interface My_Interface<T>{//泛型接口
void Method(T t);
}
class My_Class implements My_Interface<Integer>{//子类实现接口,给出具体类型
@Override
public void Method(Integer t) {
System.out.println(t);
}
}
类型擦除
JDK1.5以后引入了类型擦除的概念,类型擦除的意思大概是这样的
泛型信息只存在于代码的编译阶段,在进入JVM之前,与泛型相关的信息都会被擦除掉,专业点来说,这就叫做类型型擦除。所以说,普通的类与泛型类在JVM里基本是一样的。
通过下面的代码来演示一下类型擦除
class My_Class<T extends String,E>{
private T massaget;
private E massagee;
}
public class Main {
public static void main(String[] args) {
My_Class<String, Integer> my_class = new My_Class<>();
Class cls = my_class.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType());
}
}
}
结果为:
结论:在类型被擦除的时候,之前泛型类中的类型参数如果没有指定上限,则会被转成Object类型,如果给定了上限,那么就会被替换成类型上限。
我的总结先到这里,后续我会继续补充。