java 范型的那点事

1. 泛型概念

顾名思义,类型参数化(Generics)

2.未检查的类型转换

  给一个原生类型赋值一个泛型类型

Box rawBox = new Box();          // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;    // warning: unchecked conversion

 使用一个原生类型引用调用一个泛型类型引用的泛型方法时

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);                   // warning: unchecked invocation to set(T)

3.有界泛型类型

上届:

public class NaturalNumber<T extends Integer> {
    private T n;
    public NaturalNumber(T n)  { this.n = n; }
    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }
    // ...
}

下届:

public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

4.多重泛型类型

class A {}

interface B {}

interface C {}

//这里注意 A&B&C 参数列表中,A类型必须在最前面,因为它是一个类类型
class D<T extends A & B & C> {

private D d;

public D get() {
    return d;
}
}

class E extends A implements B, C {}

void test() {
    new D<E>().get();
}

5. 泛型与继承

我们有下面的可编译通过的代码

Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;   // OK

public void someMethod(Number n) { /* ... */ }
someMethod(new Integer(10));   // OK
someMethod(new Double(10.1));  // OK

但是下面的代码将不能通过编译

public void boxTest(Box<Number> n) { /* ... */ }
boxTest(new Box<Integer>());   //Error
boxTest(new Box<Float>());     //Error

亦即:

上面的图中可以得到:

Box<Number>和Box<Integer>之间没有什么关系,但是它们却都是Object的子类。

这也是对我们经常书写下面代码时不能通过编译的再现(javac 这样处理的原因请参考下面的类型擦除部分)

class A {}
class B  extends A{}void test() {
    ArrayList<A> list = new ArrayList<B>(); // Error
}

但是你可以使用下面这样的逻辑,这里体现了泛型中的继承思想:

           

6. 通配和子类型

这也是一种泛型体现继承的方式:

上届通配符,有时候想定义一个方法,想让这个方法给满足指定泛型类型的所有参数子类调用,这时我们可以尝试类似下面的代码:

public static void process(List<? extends Foo> list) {
    for (Foo elem : list) {
        // ...
    }
}

这样,将会有如下的代码通过编译:

    interface Foo {

    }

    class A implements Foo {
    }

    class B implements Foo {
    }

    public static void process(List<? extends Foo> list) {
        for (Foo elem : list) {
        }
    }

    void test() {
        process(new ArrayList<A>()); // ok
        process(new ArrayList<B>()); // ok
    }

    // 久违的 纳尼
    List<? extends  Foo>  list=new ArrayList<A>();
       

更玄乎的是:

List<?> 是任何 List<T>的父类型,其中T是一个具体类型。

只是它的使用场景是具体的泛型类型中的成员方法不依赖泛型参数时。

          

7.通配类型的捕获

import java.util.List;

public class WildcardError {

    void foo(List<?> i) {
        i.set(0, i.get(0));   //不能确定的类型,所以i.get()得到的元素不能被保存
    }
}

需要提供给一方法来辅助识别泛型参数,使得通过编译

public class WildcardFixed {

    void foo(List<?> i) {
        fooHelper(i);
    }
    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

}

下面的这段代码是不能通过编译的

List<Integer> list = new ArrayList<Integer>();
List<? extends Number> list2 = list;// ok
list2.add(new Integer(1)); // compile-time error

8.类型擦除

下面这段文字是引自java官方文档

Type Erasure

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

泛型中的helper方法保证继承中的多态

public class Node {

    private Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println(Integer data);
        super.setData(data);
    }
}

// MyNode在被javac编译后有如下的内容

class MyNode extends Node {

    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }

    // ...
}

使用javap对MyNode.class 反编译可以得到具体字节码内容。

时间: 2024-11-06 09:34:18

java 范型的那点事的相关文章

Java范型随笔

最近在帝都好无聊啊, 排遣寂寞就只有让自己不要停下来,不断的思考了 QWQ; 最近做ndk, java有点忘了,突然看到了一些java范型方面的问题, 踌躇了一会, 想着想着,决定还是写个随笔记录下来. 范型语法这个网上找度娘可以要到一大把, 我就不记了, 主要是范型上下限问题. 案例: public class Test { public static class Base{ } public static class A extends Base{ } public static class

JAVA范型-基础

JAVA范型-基础 一.泛型的概念 1.实现了参数化类型 2.用于编写可应用于多种类型的代码,即所编写的代码可应用于许多许多的类型. 3.范型容器.范型接口.范型方法都是经典的用法. 二.泛型与多态 1.多态是一种泛化机制.在使用类型说明的地方,使用多态确实具备一定的灵活性.但,多态是受限制的:只能接受基类或其子类(拘泥于单继承体系,使程序受限太多).而且,在实际编写代码时,只能使用已存在的基类或接口.同时,一旦指明了接口,程序就会要求你的代码必须使用特定接口(方法). 2.泛型:使我们能够编写

由Cannot create a generic array of ArrayList&lt;xx&gt;引出的学习--Java范型

最近在用Java写某个程序时希望写出一个包括ArrayList<xx>的数组 自己定义如下: ArrayList<edge>[] edges = new ArrayList<edge>()[10]; 然后编译器报错如下: Cannot create a generic array of ArrayList<edge>; 在C++里可以这样比较方便的定义:  vector<edge> a[100]; 在Java里报错,查询了一下,见已经有人在sta

java范型集合中的成员排序

范型集合中的类是JsonObject,不是自定义类,如果是自定义类就直接取要比较的字段值. 1 ArrayList<JSONObject> TList = new ArrayList<JSONObject>(); 2 3 for(int i=0;i<10000;i++) 4 { 5 JSONObject object=new JSONObject(); 6 Random rand = new Random(); 7 int randNum = rand.nextInt(200

Java 范型中的super和extends的区别

其实一句话就是: super最好用来作为输出参数,extends最好用来作为输入参数: 看看下面的例子: private class A { // } private class B extends A { // } private class C extends B { // } public void test() { List<? super B> test = new ArrayList(); test = new ArrayList<A>(); test = new Ar

Java范型之T extends Comparable&lt;? super T&gt;

在观察Java源码的时候,发现了这么一个写法T extends Comparable<? super T>.不禁纳闷为什么要这么写呢?有什么好处吗,extends和super在这里的作用着实让人有点不清楚. 接下来,我将结合代码跟大家分享一下我关于这里泛型应用的看法. 1.<T extends Comparable<? super T>>代表什么意思 大家可以明白的是这里应用到了Java的泛型,那么首先向大家说明一下这里extends的作用 extends后面跟的类型,

Java千百问_05面向对象(014)_如何获取范型的类Class

点击进入_更多_Java千百问 1.如何获取范型的类Class java中,无法获取范型的类型,例如: public class Box<T> { public static void main(String[] args) { System.out.printf(T);//编译错误 } } 其实,由于java是强类型语言,在编译时我们并不知道T是什么具体类型,只有在编译后,不同场景指定之后才会知道,所以在编译前是无法获取T的类型.如果想获取T的类型,可以在泛型类中声明一个对象,通过对象获取当

java解惑之常常忘记的事

java解惑之常常忘记的事 2012-10-17 18:38:57|  分类: JAVA |  标签:基础知识  软件开发  |举报|字号 订阅 针对刚接触java的菜鸟来说,java基础知识都是我们必须认真学习的,但是在工作过几年时间的老鸟来说,有时候也会对java的基础知识产生疑问,对于这种不确定,并且很容易混淆的知识点,java解惑已经为大家进行了很好的总结,现在借用一个作者的总结,进行一下罗列,希望能对你有所帮助. 1. 奇偶判断 不要使用 i % 2 == 1 来判断是否是奇数,因为i

分布式计算(一)基本范型

对于分布式的概念既模糊又有些理解,总是无法很好的把握,似乎与我们工作无关,但又天天在用,  究竟什么是分布式, 它能做什么?从那下手,网上可以查到很多,都使我越来越糊涂,大概是分布式计算.分布式存储.分布式缓冲等.日日梳理这些 模糊的东西,希望能有一天,能从这里走出一条光明的路. 1. 根据分布式计算抽象层次和封装层次的不同,可以将分布式的计算范型分为以下几类: 2.消息传递范型 消息传递(Message Passing) 范型利用网络API,完成将用户请求进行打包.传送和解包的工作,必要时还要