一、泛型定义
泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
我的理解是:泛型就是把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
注意:泛型里面数据类型不能是基本类型,原因:因为虚拟机在编译时会把带泛型的转换成Object类型,而基本类型不属于Object类型,所以泛型里面数据类型不能是基本类型。
二、泛型的作用
2.1、为什么要使用泛型呢?
Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
2.2、泛型只存在于编译期。
举个例子:
public class Test {
public static void main(String[] args) throws Exception{
List<String> list = new ArrayList();
list.add("hello");
//list.add(23) //编译期会报错
Class c = Class.forName("java.util.ArrayList");
Method m = c.getMethod("add",Object.class);
m.invoke(list,23);
System.out.println(list); // [hello, 23]
}
}
通过上面可知,泛型只在编译期有效,为什么运行期失效了呢,这是因为泛型的擦除概念,通俗点来说就是泛型的信息不会进行运行阶段。
三、泛型如何使用
3.1、泛型有三种实用方式
泛型类:public class Test<T>}{}
T表示未知类型
泛型接口:public interface Test<T>{}
和定义类一样
泛型方法:public <T> void Test(T name){}
3.2、泛型类的使用
泛型类在java中有着很重要的地位,其中我们用的最多的就是ArrayList,HashMap,HashSet.
//这个T可以换成随便一个字母 ,只不过我写泛型都用的T,你可以换成A,B,C...
public class Test<T> {
T name;
public Test(T name){
this.name = name;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
//如果不传泛型类型的话,那么默认的就是Object型什么都可以传
Test test = new Test("hello");
//传入的数据类型不为基本类型,否则编译期会报错,开头我解释过为什么会报错了
Test<Integer> test1 = new Test<>(418);
3.3、泛型接口的使用
泛型接口的定义和泛型类的定义差不多,我们常见的泛型接口就是,List,Map,Set.
public interface Test<T>{
T getName(T name);
}
//如果实现接口的时候不传入数据类型的话,需要将泛型声明也要写到类中要不然会报错
class Test1<T> implements Test<T>{
@Override
public T getName(T name) {
return null;
}
}
//实现接口的时候传入数据类型的话,就不用把泛型声明也写到类中了
class Test2 implements Test<String>{
@Override
public String getName(String name) {
return name;
}
}
3.4、泛型方法的使用
泛型方法的使用
public <T> void getName(T name){}
public <T,K> void getNameAndValue(T name, K value){}
public <T,K,V> void getNameAndValueAndV(T name, K value, V v){}//总的来说就是参数需要多少泛型,返回值前面就得定义几个泛型要不然编译期会出错
3.5、泛型通配符
为什么要用通配符呢?
java里面类和类之间是有继承关系 的,比如Cat extends Animals,那么Cat就是Animal的子类,但是集合是没有继承这个概念的,比如List<Cat> catList
和List<Animals> animalList
你不能说 animalList是catList的父类,所以很难看出来这两个类之间的联系,但是我们现在只想让list里面只加入Animals的子类怎么办呢?
通配符的基本概念?
3.5.1、无边界的通配符:?
举个例子,能接收所有未知类型的泛型
public class Test {
public static void main(String []args){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
List<String> stringList = new ArrayList<>();
stringList.add("h");
stringList.add("e");
stringList.add("l");
stringList.add("l");
stringList.add("o");
getList(stringList);
getList(list);
}
//无论传入什么List都会被接收
public static List getList(List<?> list){
return list;
}
3.5.2、上边界通配符号:
可以接收E以及E的子类型的泛型,这里面的E不止是类哦,也可以是接口,看个例子。
//这个是继承了类的用法
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
getList(list);
List<String> strings = new ArrayList<>();
strings.add("hello");
getList(strings);//编译期报错
}
public static List getList(List<? extends Number> list) {
return list;
}
}
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
getList(list);// 编译期报错
List<Test2> test2s = new ArrayList<>();
getList(test2s);
}
//上边界为接口的实现,只要是实现了此接口的类都可以被当做泛型传进来
public static List getList(List<? extends Test1> list) {
return list;
}
}
interface Test1{
}
class Test2 implements Test1{}
以上可知上边界就是你传入的类型必须得是E的子类,或者是实现接口的类。
3.5.3、下边界通配符号:
就是传入的类型必须得是E以及E的父类,举个例子
public class Test {
public static void main(String[] args) {
List<Animals> animals = new ArrayList<>();
getList(animals);
List<Cat> cats = new ArrayList<>();
getList(cats);
List<Dog> dogs = new ArrayList<>();
getList(dogs);//编译出错,因为Dog不是Cat的父类
}
public static List getList(List<? super Cat> list) {
return list;
}
}
class Animals{}
class Cat extends Animals{}
class Dog extends Animals{}