泛型

1.为什么有泛型?
在jdk 1.5之前,任何类型都可以添加到集合中,这是类型不安全的,因为向下转型不安全.如下:

class Point {
 private Object x ; 
 private Object y ;
 public Object getX() {
 return x;
 }
 public void setX(Object x) {
 this.x = x;
 }
 public Object getY() {
 return y;
 }
 public void setY(Object y) {
 this.y = y;
 }
}
// 设置数据
Point p = new Point() ; 
p.setX(10); // ⾃动装箱并且向上转型为Object
p.setY(20);
// 取出数据
int x = (Integer) p.getX() ; // 强制向下转型为Integer并且⾃动拆箱
int y = (Integer) p.getY() ; 
System.out.println("x = " +x+",y = "+y);

如果设置方设置错了类型,而接收方不知道,就会出现ClassCastException错误,将两个没有关系的对象进行强转了.这时候语法并没有做出任何限制,但是程序出错了,所以说向下转型不安全,会带来隐患.例如:

// 设置数据
Point p = new Point() ; 
p.setX(10.2); 
p.setY("北纬20度");
// 取出数据
String x = (String) p.getX() ; 
String y = (String) p.getY() ; 
System.out.println("x = " +x+",y = "+y);

2.泛型的使用
在jdk 1.5中引入泛型,只有指定类型才可以添加到集合中,而且读取对象不用进行向下转型,隐患被消除了.具体语法是:在类声明时通过一个占位符来表示类型,值类实例化时指定需要的类型.

class MyClass<T> {
 T value1;
}
MyClass<String> myClass1 = new MyClass<String>();
MyClass<Integer> myClass2 = new MyClass<Integer>();

泛型只能使用类,所有的基本数据类型必须进行装箱.
Java为每个基本数据类型都提供了对应的包装器类型,可以实现自动装箱和自动拆箱.
1)什么是自动装箱?
原来:

Integer i = new Integer(10);

自动装箱:

Integer i = 10;

以上过程会自动根据数值创建Integer对象,自动将基本数据类型转换为包装器类型,这就是自动装箱.

2)什么是自动拆箱?

Integer i = 10;  //装箱
int n = i;   //拆箱

如上,自动将包装器类型转换成基本数据类型,就是自动拆箱.

3)基本数据类型对应的包装器类型:
int(4字节) Integer
byte(1字节) Byte
short(2字节) Short
long(8字节) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(未定) Boolean

4)装箱和拆箱是如何实现的?
装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }
private static class IntegerCache {
        static final int high;
        static final Integer cache[];

        static {
            final int low = -128;

            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。Double、Float的valueOf方法的实现是类似的.

对象实例化时不指定泛型,默认为:Object.(类型擦除)

2.1泛型还可以用来单独定义方法

class MyClass{
 public <T> void testMethod(T t) {
 System.out.println(t);
 }
}

中的T 被称为类型参数,⽽⽅法中的 T 被称为参数化类型,它不是运⾏时真正的参数。
泛型类和泛型方法可以共存,其中泛型方法始终以自己定义的类型参数为准,为了避免混淆两者的类型参数最好不要同名.

使用泛型会导致参数不能统一的问题,如下:

class Message<T> {
 private T message ;
 public T getMessage() {
 return message;
 }
 public void setMessage(T message) {
 this.message = message;
 } 
}
public class TestDemo {
 public static void main(String[] args) {
 Message<Integer> message = new Message() ; 
 message.setMessage(99);
 fun(message); // 出现错误,只能接收String
 }
 public static void fun(Message<String> temp){
 System.out.println(temp.getMessage());
 }
}

为了解决参数不统一的问题引入了通配符.

3.通配符
使用通配符可以接收所有参数,又能让用户不可以随意修改类型.

public class TestDemo {
 public static void main(String[] args) {
 Message<Integer> message = new Message() ; 
 message.setMessage(55);
 fun(message); 
 }
 // 此时使⽤通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以⽆法修改
 public static void fun(Message<?> temp){
 //temp.setMessage(100); ⽆法修改!
 System.out.println(temp.getMessage());
 }
}

3.1两个子通配符
?extends 泛型上限
?super 泛型下限

public class TestDemo {
 public static void main(String[] args) {
 Message<Integer> message = new Message() ; 
 message.setMessage(55);
 fun(message); 
 }
 // 此时使⽤通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以⽆法修改
 public static void fun(Message<? extends Number> temp){
 //temp.setMessage(100); 仍然⽆法修改!
 System.out.println(temp.getMessage());
 }
}
public class TestDemo {
 public static void main(String[] args) {
 Message<String> message = new Message() ; 
 message.setMessage("Hello World");
 fun(message); 
 }
 public static void fun(Message<? super String> temp){
 // 此时可以修改!!
 temp.setMessage("bit!");
 System.out.println(temp.getMessage());
 }
}

上限可以⽤在声明,不能修改;⽽下限只能⽤在⽅法参数,可以修改内容!

4.泛型接口
这个接口实现子类时,可以继续使用泛型,也可以使用具体类型.

interface IMessage<T> { // 在接⼝上定义了泛型
 public void print(T t) ; 
}
class MessageImpl<T> implements IMessage<T> {
 @Override
 public void print(T t) {
 System.out.println(t);
 }
 
}
public class TestDemo {
 public static void main(String[] args) {
 IMessage<String> msg = new MessageImpl() ; 
 msg.print("Hello World");
 }
}
interface IMessage<T> { // 在接⼝上定义了泛型
 public void print(T t) ; 
}
class MessageImpl implements IMessage<String> {
 @Override
 public void print(String t) {
 System.out.println(t);
 }
 
}
public class TestDemo {
 public static void main(String[] args) {
 IMessage<String> msg = new MessageImpl() ; 
 msg.print("Hello World");
 }
}

5.泛型擦除
泛型是编译时期的概念,到运行时期泛型类和普通类没有区别.

class MyClass<T>{
 private T message;
 public T getMessage() {
 return message;
 }
 public void setMessage(T message) {
 this.message = message;
 }
 public void testMethod1(T t) {
 System.out.println(t);
 }
}
public class Test {
 public static void main(String[] args) {
 MyClass<String> myClass1 = new MyClass<>();
 MyClass<Integer> myClass2 = new MyClass<>();
 System.out.println(myClass1.getClass() ==
myClass2.getClass());
 }
}

以上代码打印结果为true,因为在运行时期,myClass1和myCllass2的类都是Myclass.
在泛型类被类型擦除时,如果之前泛型类的类型参数部分没有指定上线。类型被翻译成Object类型,如果指定了上限类型,类型被翻译成指定上限类型.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值