A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 冰枫 中级黑马   /  2014-4-16 11:41  /  1048 人查看  /  4 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 冰枫 于 2014-4-16 23:01 编辑

上限:? extends E  可接收E类型或其子类
下限:? super E     可接收E类型获取父类
例如:
TreeSet (Collection<? extends E> c)
TreeSet(Comparator<? super E> comparator)

泛型的限定中的上限和下限好像有一定规律,如同上面例子中的下限限定的是Comparator ,是上限限定的Collection中的比较器,谁能用简单的语言给我阐述一下,谢谢

4 个回复

倒序浏览

JAVA中基本的泛型语法元素大致有三种:限制泛型可用类型、使得类型通配符,以及泛型的继承。下面将分别对这三种语法进行介绍。

1、限制泛型可用类型

我们在定义泛型类时,默认的是可以使用任何类型来实例化泛型类中的类型持有者。我们也可以指定某种类型,使得此泛型类只能通过这种类型或其子类,或实现这个接口的类来实例化类型持有者。

我们在定义类型持有者时,使用extends关键字来进行限制,例如我们可以这样定义泛型类:

[java]
public class LimitDemo<T extends List>
{
}

public class LimitDemo<T extends List>

{
}

这表示类型持有者T所代表的类型必须是一个实现了List接口的类型。所以我们要这样产生LimitDemo类的实例:

[java]
LimitDemo<ArrayList> demo = new LimitDemo<ArrayList>();

LimitDemo<ArrayList> demo = new LimitDemo<ArrayList>();因为ArrayList是实现了List接口的类,所以这样写是合法的。如果不是实现List接口的类:

[java]
LimitDemo<String> demo = new LimitDemo<String>(); // String没有实现List接口

LimitDemo<String> demo = new LimitDemo<String>(); // String没有实现List接口
编译器就会报出 “String类型不在T范围内” 的错误:

事实上,当我们不用extends关键字来限制泛型类对象时,编译器默认的是Object类下所有的子类都可以实例化类型持有者。即:

[java] view plaincopyprint?
public class LimitDemo<T>{}

public class LimitDemo<T>{}等价于:

[java]
public class LimitDemo<T extends Object>{}

public class LimitDemo<T extends Object>{}

2、类型通配符

如果有一个引用名demo,我们希望demo既能引用WildDemo<String>类型,又能引用WildDemo<Integer>类型,该如何实现呢?

不使用通配符的话,我们只能分别定义两个变量,一个是WildDemo<String> demo1,另一个是WildDemo<Integer> demo2,来保存对这两种类型的实例的引用。如果使用通配符,则可以这样写:

[java]
// 使用通配符 '?'  
        WildDemo<?> demo;
         

    demo = new WildDemo<Integer>();
        demo = new WildDemo<String>();

// 使用通配符 '?'
        WildDemo<?> demo;
      
        demo = new WildDemo<Integer>();
        demo = new WildDemo<String>();
但要注意的是,通过使用类型通配符声明的名称所引用对象,无法对该对象添加信息,只能获取或移除该对象的信息。如下例:

[java]
import java.util.*;

public class WildDemo<T>
{
    private T x;
     
    // 为x赋值  
    public void setX(T x)
    {
        this.x = x;
    }
    // 读取x的值  
    public T getX()
    {
        return x;
    }
     
    public static void main(String[] args)
    {
        WildDemo<Integer> obj = new WildDemo<Integer>();
        obj.setX(100);
         
        // 使用通配符 '?'  
        WildDemo<?> demo = obj;
        demo.getX(); // 读取信息,OK  
        demo.setX(null); // 移除信息,OK  
        demo.setX(200); // 设置新的信息给X, ERROR  
    }
}

import java.util.*;

public class WildDemo<T>
{
    private T x;
   
    // 为x赋值
    public void setX(T x)
    {
        this.x = x;
    }
    // 读取x的值
    public T getX()
    {
        return x;
    }
   
    public static void main(String[] args)
    {
        WildDemo<Integer> obj = new WildDemo<Integer>();
        obj.setX(100);
      
        // 使用通配符 '?'
        WildDemo<?> demo = obj;
        demo.getX(); // 读取信息,OK
        demo.setX(null); // 移除信息,OK
        demo.setX(200); // 设置新的信息给X, ERROR
    }
}

3、泛型类的继承

定义父泛型类:


[java]
class Parent<T1,T2>{}

class Parent<T1,T2>{}定义子泛型类从Parent继承而来:


[java]
class Child<T1,T2,T3> extends Parent<T1,T2>{}

class Child<T1,T2,T3> extends Parent<T1,T2>{}
如果子类想保留父类的类型持有者T1,T2,那么父类上所声明的类型持有者的数目在继承下来时必须写全,即class Child<T1,T2)>。如果子类不保留类型持有者,那么继承下来的T1,T2自动变为Object类型。


在实际应用中我们使用泛型应该本着简洁易读的原则来定义泛型类,要尽量避免定义有多重<>的复杂泛型。


评分

参与人数 1技术分 +1 收起 理由
菜小徐 + 1

查看全部评分

回复 使用道具 举报
TreeSet (Collection<? extends E> c):这个上限是指Collection集合中只能接收E类及其子类,并且在这个类中,所有对象都将视作E类型;
TreeSet(Comparator<? super E> comparator):这个下限是指Comparator比较器仅比较E类及其所有父类的对象,在这个比较器中,所有对象都将视作Object类型,因为Object是所有类的超类。
回复 使用道具 举报
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
泛型还有接口、方法等等,内容很多,需要花费一番功夫才能理解掌握并熟练应用。在此给出我曾经了解泛型时候写出的两个例子(根据看的印象写的),实现同样的功能,一个使用了泛型,一个没有使用,通过对比,可以很快学会泛型的应用,学会这个基本上学会了泛型70%的内容。
例子一:使用了泛型
class Gen<T> {
private T ob; //定义泛型成员变量
public Gen(T ob) {
this.ob = ob;
}
public T getOb() {
return ob;
}
public void setOb(T ob) {
this.ob = ob;
}
public void showType() {
System.out.println("T的实际类型是: " + ob.getClass().getName());
}
}
public class GenDemo {
public static void main(String[] args){
//定义泛型类Gen的一个Integer版本
Gen<Integer> intOb=new Gen<Integer>(88);
intOb.showType();
int i= intOb.getOb();
System.out.println("value= " + i);
System.out.println("----------------------------------");
//定义泛型类Gen的一个String版本
Gen<String> strOb=new Gen<String>("Hello Gen!");
strOb.showType();
String s=strOb.getOb();
System.out.println("value= " + s);
}
}
例子二:没有使用泛型
class Gen2 {
private Object ob; //定义一个通用类型成员
public Gen2(Object ob) {
this.ob = ob;
}
public Object getOb() {
return ob;
}
public void setOb(Object ob) {
this.ob = ob;
}
public void showTyep() {
System.out.println("T的实际类型是: " + ob.getClass().getName());
}
}
public class GenDemo2 {
public static void main(String[] args) {
//定义类Gen2的一个Integer版本
Gen2 intOb = new Gen2(new Integer(88));
intOb.showTyep();
int i = (Integer) intOb.getOb();
System.out.println("value= " + i);
System.out.println("---------------------------------");
//定义类Gen2的一个String版本
Gen2 strOb = new Gen2("Hello Gen!");
strOb.showTyep();
String s = (String) strOb.getOb();
System.out.println("value= " + s);
}
}
运行结果:
两个例子运行Demo结果是相同的,控制台输出结果如下:
T的实际类型是:
java.lang.Integer
value= 88
----------------------------------
T的实际类型是: java.lang.String
value= Hello Gen!
Process finished with exit code 0
看明白这个,以后基本的泛型应用和代码阅读就不成问题了。
回复 使用道具 举报
楼主好像理解错了吧,这里的意思是,第一个是创建一个TreeSet,使用的是 (包含E或其子类对象)的集合,括号里的是泛型限定.
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马