我们先来看个例子
//定义一个泛型类,并定义如下两个方法
class Test
{
public void show(T t){
System.out.println(t);
}
public void print(T t){
System.out.println(t);
}
/* 以前是这样定义,现在一个方法搞定
public void show(String t){
}
public void show(Integer t){
}
或者
public void show(Object obj){
}
*/
}
//在manin方法代码如下
public static void main(String[] args) {
Test d = new Test();
d.show("java");
d.print("Object-C");
Test e = new Test();
e.show(2);
e.print(new Integer(5));
}
上面是一个简单的代码demo,运行没问题,正常输出。但是你会发现,其实代码有点冗余。我们定义了一个泛型类,并定义了 show(T t)和print(T t)方法。
发现: 泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所要操作的类型已经固定了。就像上面main方法中。对象d,只能操作String类型,如果你要操作其他类型,只能额外去创建其他泛型对象e。
设想下,如果我能把泛型定义在方法上,这样不就可以优雅解决问题。于是变化代码如下
//定义一个类,并定义如下两个泛型方法
class Test
{
public void show(T t){
System.out.println(t);
}
public void print(T t){
System.out.println(t);
}
public void sum(U u,T t){
System.out.println(u+" version is "+t);
}
}
// main方法如下
public static void main(String[] args) {
Test d = new Test();
d.show("java");
d.print(5);
d.sum("java", new Double(8));
}
Test不再是泛型类,泛型方法show(T t)、print(T t)、sum(U u,T t) 更具有扩展性。
访问修饰符 [static][final] <类型参数列表> 返回值类型 方法名([形式参数列表])
备注:[] 代可有可无的意思,泛型方法可以是实例方法或静态方法,类型参数可以在静态方法中,这是与泛型类最大的区别。
interface 接口名<类型参数表>
定义一个泛型接口
interface showInterface
{
public void show(T t);
}
(1) 实现类确定了类型
class ShowClass implements showInterface{
public void show(String t){
System.out.println("show:"+t);
}
}
(2) 实现类类型不确定
class ShowClass implements showInterface{
public void show(T t){
System.out.println("show:"+t);
}
}
main方法
public static void main(String[] args) {
ShowClass obj = new ShowClass();
obj.show(6);
/*
ShowClass obj = new ShowClass();
obj.show("java");
*/
}
看个例子就明白了。定义两个集合,分别输出两个集合的元素。
public static void main(String[] args) {
ArrayList a1 = new ArrayList();
a1.add("a");
a1.add("b");
a1.add("c");
ArrayList a2 = new ArrayList();
a2.add(1);
a2.add(2);
a2.add(3);
}
在我们没学习泛型之前,我们会封装静态方法如下:
public static void printList(ArrayList list){
for (Iterator iterator = list.iterator(); iterator.hasNext();)
{
Object object = (Object) iterator.next();
System.out.println(object);
}
}
代码买啥毛病,运行也正确。会有一个疑问。为什么参数没定义泛型,但是却可以接受泛型呢?泛型是jdk1.5出来的,老版本肯定要兼容新版本。
在我们学习泛型方法后,我们进一步将代码修改如下:
public static void vistor(ArrayList a){
Iterator iterator = a.iterator();
while(iterator.hasNext()){
T t = iterator.next();
System.out.println(t);
}
}
定义一个泛型方法。如果是泛型类,是不允许泛型定义在static上面的。
如果不想定义泛型方法,又能够解决问题呢?这就要用到一个通配符的玩意
//占位符,也称为通配符。表示元素类型可以匹配任意类型
public static void sop(ArrayList a){
for(Iterator it = a.iterator();it.hasNext();){
System.out.println(it.next());
}
}
泛型方法T如果是具体类型的话,可以接收T t = iterator.next();。?是占位符,不明确具体类型,无法接收。
这种带通配符ArrayList a的List 仅仅表示他是各种泛型的父类,并不能把元素添加到其中。
ArrayList list = new ArrayList();
list.add(null);
上面说,带通配符ArrayList a的List 仅仅表示他是各种泛型的父类,并不能把元素添加到其中。是不是想违背了?仔细想想,null是任意引用类型的实例。这比较特殊。
同样看一个例子。 定义一个集合,遍历元素的方法并输出。
//定义一个Person类
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getNmae() {
return this.name;
}
// 定义一个Student 并继承 Person
static class Student extends Person {
Student(String name) {
super(name);
}
}
public static void main(String[] args) {
ArrayList a1 = new ArrayList();
a1.add(new Person("abc1"));
a1.add(new Person("abc2"));
a1.add(new Person("abc3"));
printMethod(a1);
// 下面是错误的。an存的是Person,类型安全问题,左右两边要一致
// ArrayList an = new ArrayList();
//泛型是父类,可以存子类
ArrayList a = new ArrayList();
a.add(new Student("abc--1"));
a.add(new Student("abc--2"));
a.add(new Student("abc--3"));
//错误,因为要求是Person,而传入的是Student
//printMethod(a);
//使用通配符可以。
printMethod2(a);
//泛型是父类,可以存子类
ArrayList a2 = new ArrayList();
a2.add(new Student("abc--1"));
a2.add(new Student("abc--2"));
a2.add(new Student("abc--3"));
printMethod(a2);
//如果我想调用也printMethod(a2);没毛病。怎么做?。 第一想法直接给占位符。但是带来一问题,不能调用具体方法。
}
// 与main方法同级的静态工具类方法
public static void printMethod(ArrayList a1) {
Iterator it = a1.iterator();
while (it.hasNext()) {
System.out.println(it.next().getNmae());
}
}
// 与main方法同级的静态工具类方法
public static void printMethod2(ArrayList a1) {
Iterator it = a1.iterator();
while (it.hasNext()) {
System.out.println(it.next().getNmae());
}
}
}
如果我想调用也printMethod(a2); 第一想法直接给占位符。但是带来一问题,不能调用具体方法。泛型也是一样的。如果我们想两者兼得,既能打印Stuednt,也能打印Person,我们将printMethod修改如下
public static void printMethod(ArrayList a1){
Iterator it = a1.iterator();
while(it.hasNext()){
System.out.println(it.next().getNmae());
}
}
我们称? extends T 为 泛型上限定: 可以接收T和T的子类
接收T类型或者T的父类型
从集合TreeSet的构造方法我们可以看到 下限定的运用。我们也可以集合实际例子。
TreeSet ts = new TreeSet(); //Comparator comparator
ts.add(new Person("d"));
ts.add(new Person("f"));
ts.add(new Person("g"));
比较器如下// 这边的T写Student和Person都是可以的
class comp implements Comparator
{
public int compare(Person s1,Person s2){
return s1.getNmae().compareTo(s2.getNmae());
}
}
其实限定的目的是为了扩展指定类型。
通过上面比较器参数和我们实现的比较器。如果我们比较器的泛型为 Student,那么只能比较Student。当未来某一天,Person有新子类出现的话,那么该比较器就不适用了。所以Java 的API 的考虑扩展性的同时,已经设置了泛型下限定,你可以传T类型或者T的父类型。
参考:https://blog.csdn.net/qq_18505715/article/details/88381607