【Java】语法特性之泛型

Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。(《Java编程思想--第15章》)


一、简单泛型

1.Holder1只接受int类型的参数

public class Holder1{
    private int a;
    public class Holder(int a){this.a = a ;}
    public Holder1 get(){return a;}
    public void set(int a){this.a = a;}
}

2.Holder2接收Object类型的参数,可以接收Object类型和所有继承自Object类型的参数

public class Holder2{
    private Object a;
    public Holder2(Object a){this.a=a;}
    public Holder2 get(){return a;}
    public void set(Object a){this.a=a;}

    public static void main(String[] args){
        Holder2 h2=new Holder2(1);
        Integer i=(Integer)h2.get();
        h2.set(new String("abc"));
        String s = (String)h2.get();
    }
}

3.Holder3先不指定类型,而是稍后再决定具体使用什么类型

public class Holder3<T>{
    private T a;
    public Holder3(T a){this.a=a;}
    public T get(){return a;}
    public void set(T a){this.a=a;}

    public static void main(String[] args){
        Holder3 h3 = new Holder3(new String("abc"));
        //h3.set(1);//会报错
    }
}

原则:无论何时,只要能做到,就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么久应该只使用泛型方法。


二、泛型方法

对一个static的方法,无法访问泛型类的类型参数。所以,要定义泛型化方法,需要将泛型参数列表置于返回值之前。

但是这个output方法,并不是一个static类型的。


下图,参数列表由T,K组成



三、extends和super的含义

先声明三个类Human Father Son

//Human.java

public class Human{}

//Man.java

public class Man extends Human{}

//Father.java

public class Father extends Man{
	int a;
	static int count=0;
	public Father(int a){
		this.a=a;
		count++;
	}
}


1.<? extends Man>上界,先写段代码感受一下


!!! 跟猜想的完全不一样有木有? extends Father并不接受 Father的子类,甚至不接受Object类型,只接受null。

原因分析:? extends Father代表的是Father和继承自Father的类型,但是编译器并不知道具体会放入什么类型的变量,所以就都不接收。只有null可以代表所有类型,所以可以被接受。


2.<? super Man>下界,再写段代码,感受一下


依然跟猜想的不太一样, <? super Man>的意思并不是接受Man的基类,而是接受Man和Man的子类类型。

总结起来就是,编译器可以自动完成向上转型,但无法自动向下转型。比如这个list2,Father一定是Man,而Human不一定是Man类型。


3.现在知道了super和extends的区别,来研究一下extends的用法。

extends不能用于add,但是可以用于get,先准备好三个方法备用:

        public static ArrayList<Human> getHumanList(){//获取一个Human类型的list
		ArrayList<Human> humans = new ArrayList<>();
		humans.add(new Man());
		humans.add(new Man());
		humans.add(new Father(1));
		return humans;
	}
	
	public static ArrayList<Man> getMenList(){//获取一个Men类型的list
		ArrayList<Man> men = new ArrayList<>();
		men.add(new Man());
		men.add(new Man());
		men.add(new Father(1));
		return men;
	}
	
	public static ArrayList<Father> getFatherList(){//获取一个Father类型
		ArrayList<Father> fathers = new ArrayList<>();
//		fathers.add(new Man());
//		fathers.add(new Man());
		fathers.add(new Father(1));
		return fathers;
	}

可能有人和我有一样的疑问,list没法add,那get什么呢?这就用到了上面的三个方法

可以直接把一个Man类型的队列赋值给list1.

① list1 = getMenList()不报错


②list1 = getFatherList()没问题(编译器可以向上转型,把Father向上转型为Man,转型后变量a的值未丢失)


③list1 = getHumanList()会报错,还是一样的原因,Human类型的数组无法保证是Man类型的。



4.super的用法

get到的只能是Object类型,向下转型需要强转,因为编译器不知道get到的是一个什么类型的变量。

不过有个地方我没太懂。这个地方get到的类型,为什么不能用Man接着就好了?(因为添加的都是Man和Man的子类类型啊)为什么一定要用Object呢?


PECS原则总结

1.如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)

2.如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)

3.如果既要存又要取,那么就不要使用任何通配符。


猜你喜欢

转载自blog.csdn.net/crab0314/article/details/80348990
今日推荐