理解:泛型又称为参数化类型。在类声明或者实例化时只要定好需要的具体的类型即可。 java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常,同时,代码更加简洁,健壮。 泛型的作用是可以在类声明时通过一个标识表示类中某个属性的类型,或者某个方法返回值的类型,或者是参数的类型.即泛型可以称为是参数类型的类型。
1.泛型的作用:
传统方法不能对加入集合的数据的数据类型进行约束(不安全) 遍历的时候,需要进行类型转换,如果数据量很大,对效率有影响 解决:泛型
首先创建一个Dog类和一个Cat类:
class Cat{
String name;
public Cat(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
class Dog{
String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("旺财"));
dogs.add(new Dog("进宝"));
dogs.add(new Dog("大黄"));
如果在ArrayList中使用泛型(就是在<>中写上类的名字,表明该集合中的类型都是Dog类的对象)就不能在添加其他类型的对象加入ArrayList中。并且在使用增强for循环等语句中,省去了判断对象运行类型什么的语句。提高了效率
for (Dog o :dogs) {//取出时直接可以转换成Dog,减少向下转型造成的效率损失
System.out.println(o);
}
2.泛型的声明
泛型的声明: interface 接口<T>{}和class 类<K,V>{} (1)其中T,K,V不代表值,而是表示类型 (2)任意字母都可以,常用T(type)表示
细节: 1.interface 接口<T>{}和class 类<K,V>{}等等 说明:T,K,V只能是引用类型,基本数据类型会报错 2.在给泛型制定了具体类型后,可以传入指定类型或者指定类型的子类型 3.泛型的使用类型 实际开发中,一般使用简写形式: Pig<AA> aPig = new Pig<>(new AA()); 4.如果不写泛型,默认的泛型就是Object ArrayList arrayList = new ArrayList();
举例泛型类:
class A<E>{
//该数据类型是在编译期间知道E是什么类型了
E s;//一个E类型的s
public E f()//定义返回值的类型为E
{
return s;
}
public A(E s) {
this.s = s;
}
public void show()
{
System.out.println(s.getClass());
}
}
A<String> hh = new A<String>("臭蛋慧");
hh.show();//s运行类型是String
A<Integer> integerA = new A<Integer>(100);
integerA.show();//s运行类型是Integer
3.泛型类
理解泛型类和类中使用到了泛型,两者的区别:最大的区别是类在定义时有没有<>.
class 类名<T,R,...>{成员} 细节: 1.普通成员可以使用泛型(属性,方法) 2.使用泛型的数组不能初始化 3.静态方法中不能使用类的泛型 4.泛型类的类型,是在创建对象时确定的(因为创建对象,需要指定确定类型) 5.如果在创建对象时没有指定类型,默认为Object
//Tiger后面泛型,我们把Tiger称为泛型类
class Tiger<T,R,M>{
String name;
T a;
R b;
M c;//属性使用泛型
//方法使用泛型
public T show(R a1,M a2)
{
System.out.println(b);
return a;
}
//静态成员使用泛型时报错,因为静态成员的加载是在类加载的时候,而泛型的确定是在创建对象时。所以不能使用,
// static T r2;
// public static void f(M r1)
// {
//
// }
public Tiger(String name, T a, R b, M c) {
this.name = name;
this.a = a;
this.b = b;
this.c = c;
}
//T[] arr = new T[8];//报错,不能初始化。因为数组在new时不能确定类型,所以无法开辟空间
}
4.泛型接口
自定义泛型接口: 1.接口中静态成员不能使用泛型 2.继承接口的类型,在继承接口或者实现接口时确定 3.实现泛型接口时直接指定泛型的类型 4.也可以不指定类型,默认是Object,但是建议哪怕是默认,也要把Object加上。
规则1:
//1.静态成员不能使用泛型
interface IUsb<U,R>{
//普通方法中可以使用接口泛型
int n = 100;//接口中所有的属性都是默认使用static final修饰的
//R n2;//因为静态成员不能使用泛型,所以就是接口中属性不能使用泛型
R get(U u);
void run(U u1,U u2,R r1,R r2);
}
规则2:继承接口时直接指定类型。这里IUsb的U和R就已经指定为String和Double
//2.在继承接口,指定泛型接口的类型
interface IA extends IUsb<String,Double>
{
int n = 200;
}
然后如果有其他类继承IA。其中的泛型已经自动被替换
class AAA implements IA{
//因为继承接口制定了泛型接口的泛型类型,所以继承了继承接口的类实现方法时,可以直接使用指定的类型来替换泛型。
@Override
public Double get(String s) {
return null;
}
@Override
public void run(String u1, String u2, Double r1, Double r2) {
}
}
规则3:实现接口时直接指定类型
class BBB implements IUsb<String,Integer>
{
@Override
public Integer get(String s) {
return null;
}
@Override
public void run(String u1, String u2, Integer r1, Integer r2) {
}
}
5.自定义泛型
1.自定义泛型方法
基本语法:
修饰符 <T,R...>返回类型 方法名(参数列表){}
细节: 1.泛型方法可以定义在普通类中,也可以定义在泛型类中
2.当泛型方法被调用时,类型会确定
3.public void eat(E e){} 修饰符后没有<T,R>,方法不是泛型方法,而是使用了泛型
class Car{
//普通方法
public void run(){
}
//这个方法不是泛型方法,而是方法使用了泛型
public void eat(E e)
{
System.out.println(e.getClass());
}
//泛型方法,定义了泛型就要用的,不然就没有意义了
public <T,R> void fly(T t,R r)
{
System.out.println(t.getClass().getSimpleName() + " " + r.getClass().getSimpleName());
}
}
Car car = new Car();
//传入形参时,编译器就会确定对应的类型
car.fly("宝马",100);//输出String和Integer
6.泛型的继承与通配符
泛型不具备继承性 <?>:支持任意泛型类型 <? extend A>:支持A类及A类的子类。规定了泛型的上限 <? super A>:支持A类及A类的父类。规定了泛型的下限
对于泛型不具备继承性的理解是,泛型是不能进行向上转型和向下转型的,
可以使用假设的方式证明:
1.举例:
子类泛型可以转型为父类泛型。那么子类中集合bb应该可以存放父类的类型以及除了自身的其他父类子类的类型。然而对于bb来说却只能存放BBBB类型。
class CCCC {
}
class AAAA extends CCCC{
}
class BBBB extends AAAA{
}
public class TestGeneric {
public static void main(String[] args) {
ArrayList<AAAA> aa =new ArrayList<>();
ArrayList<BBBB> bb =new ArrayList<>();
//子类泛型转父类泛型
aa = bb;//报错,
//父类泛型转换为子类泛型
bb = aa;//报错
}
}
? extends AAAA只能传入AAAA或者AAAA子类的类型。规定了泛型的上限
? supersAAAA 表示只能传入AAAA或者AAAA的父类类型。规定了泛型的下限
class Practice{
public static void main(String[] args) {
//报错,即泛型不具备继承性
//ArrayList<Object> arrayList = new ArrayList<String>();
ArrayList<AAAA> aaaas = new ArrayList<>();
ArrayList<Object> arrayList = new ArrayList<>();
ArrayList<String> strings = new ArrayList<>();
ArrayList<BBBB> bbbbs = new ArrayList<>();
ArrayList<CCCC> ccccs = new ArrayList<>();
printCollection1(aaaas);//上面的所有都能使用,因为可以传入任意类型的List
//printCollection2(ccccs);//只能传入AAAA类型或者AAAA类型的子类
//printCollection3(bbbbs);//只能传入AAAA或者AAAA类型的父类
}
public static void printCollection1(List<?> l)
{
Iterator<?> iterator = l.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
//泛型可以转入AAAA及AAAA的子类类型
public static void printCollection2(List<? extends AAAA> l)
{
for (Object o :l) {
System.out.println(o);
}
}
//泛型可以传入AAAA及AAAA的父类类型
public static void printCollection3(List<? super AAAA> l)
{
for (Object o :l) {
System.out.println(o);
}
}
}