Java笔记:泛型

泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作泛型时需要指定一个参数。

通过使用泛型,就可以在编译期防止将错误类型的对象放置到容器中(可以事先指定泛型的类型)。泛型只有在编译时期才有作用,泛型的背后实际上是Object,在编译期间进行类型检查,避免类型出错。

  1. 泛型的意义
    a:在存放数据的时候,对数据进行类型检查
    b:不需要进行强制类型转换,泛型可以自动进行类型的转换
  2. < T > :T是一个占位符,表明当前类是一个泛型类
  3. 不能new T类型的数组:不能new泛型类型的数组。
  4. 泛型的参数,不能是简单类型,只能引用类型。
  5. 泛型到底怎么编译的
    泛型真正起作用的时候,是在编译时期。
    泛型是在编译的时候的一种机制——擦除机制。
MyStack<Integer> myStack = new MyStack<>();
MyStack<String> myStack = new MyStack<>();

在编译时,会拿着Integer、Sting等引用类型对传入数据进行检查,而泛型的参数,并不参与类型的组成,在编译时都被擦除成了Object。
在这里插入图片描述

1 泛型方法和泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。 和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。 一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为 参数化的类 参数化的类型

 class Box<T,E> {
     private T t;
     private E e;
     public void add(T t,E e ) {
         this.t = t;
         this.e = e;
     }

     public T getT() {
         return t;
     }

     public E getE() {
         return e;
     }
     public static<E> void log(E str){
         System.out.println(str);
     }

 }

public class Main {
    public static void main(String[] args)  {
        Box<String,Integer> box = new Box<>();
        box.add("hello",302);
        System.out.println(box.getE()+" "+box.getT());
        box.log("world");
    }
}
2 类型擦除

Java 中的泛型基本上都是在编译器这个层次来实现的 。在生成的Java 字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>List<String>等类型,在编译之后都会变成List。JVM 看到的只是List,而由泛型附加的类型信息对JVM 来说是不可见的。 类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的上界类。

3 泛型类的栈:
class MyStack<T>{
    public T[] elem;
    public int top;
    public MyStack() {
        //this.elem = new T[10];
	//new数组时要用new Object
        this.elem = (T[]) new Object[10];
        this.top = 0;
    }
    public void push(T data) {
        this.elem[top++] = data;
    }
    public T pop() {
        T oldData = this.elem[top-1];
        top--;
        return oldData;
    }
}

public static void main(String[] args) {
    MyStack<Integer> myStack = new MyStack<>();
    myStack.push(1);
    int a = myStack.pop();
    System.out.println(a);

    MyStack<String> myStack2 = new MyStack<>();
    myStack2.push("wang");
    String s = myStack2.pop();
    System.out.println(s);

    System.out.println(myStack);//MyStack@16d3586
    System.out.println(myStack2);
}
结果为:
  MyStack@154617c:类名+哈希值,并不包含<String>,
  说明泛型的参数并不参与类型的组成。

注:
此处 MyStack<Integer> myStack = new MyStack<>();的Integer即指定了栈中的数据类型,在向栈中增加数据时,会拿着Integer做数据类型检查。

4 泛型链表:
public class TestLink<T> {
    static class Node<T> {
        public T data;
        public Node<T> next;

        public Node() {
        }
        public Node(T data) {
            this.data = data;
        }
    }
    
    public Node<T> head = null;
    
    public void insertHead(T data) {
        Node<T> node = new Node<>(data);
        if(head == null) {
            head = node;
        }else {
            node.next = head;
            head = node;
        }
    }
    public void insertTail(T data) {
        Node<T> node = new Node<>(data);
        if(head == null) {
            head = node;
        }else {
            Node<T> cur = this.head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }
    public void show() {
        Node<T> cur = this.head;
        while (cur != null) {
            System.out.print(cur.data+" ");
            cur = cur.next;
        }
        System.out.println();
    }
    public void show2(Node<T> newHead) {
        Node<T> cur = newHead;
        while (cur != null) {
            System.out.print(cur.data+" ");
            cur = cur.next;
        }
        System.out.println();
    }
}
5 单链表:两个有序链表合并

T extends Comparable:Comparable为泛型T的上界,表示传入的参数必须是实现Comparable接口的。

public class TestDemo {
//TestLink.Node<T>为返回值类型
  public static<T extends Comparable<T>> TestLink.Node<T>
      mergeList(TestLink.Node<T> headA,TestLink.Node<T> headB)
       {
        /*实例内部类
        TestLink testLink = new TestLink();
        TestLink.Node newHead = testLink.new Node(-1);*/
        //静态内部类实现
        TestLink.Node<T> newHead = new TestLink.Node<>();
        TestLink.Node tmp = newHead;
        while (headA != null && headB != null) {
            if(headA.data.compareTo(headB.data) < 0) {
                tmp.next = headA;
                headA = headA.next;
                tmp = tmp.next;
            }else {
                tmp.next = headB;
                headB = headB.next;
                tmp = tmp.next;
            }
        }
        if (headA != null){
            tmp.next = headA;
        }
        if (headB != null){
            tmp.next = headB;
        }
        return newHead.next;
    }
    
    public static void main(String[] args) {
    TestLink<Integer> testLink = new TestLink<>();
    testLink.insertTail(1);
    testLink.insertTail(3);
    testLink.insertTail(5);
    testLink.insertTail(7);
    testLink.show();
    TestLink<Integer>  testLink2 = new TestLink<> ();
    testLink2.insertTail(2);
    testLink2.insertTail(4);
    testLink2.insertTail(6);
    testLink2.insertTail(8);
    testLink2.show();

    TestLink.Node<Integer> newHead = 
    mergeList(testLink.head,testLink2.head);
    testLink.show2(newHead);
}
6 找出数组中的最大值
  • Comparable表示泛型T的上界,泛型是没有下界的。
  • T extends Comparable:表示泛型参数T一定要实现Comparable接口,此处在编译时,T被擦除成了Comparable。

类型擦除主要看其类型边界而定:

class MyArrayList<E> {
// E 会被擦除为Object
}
class MyArrayList<E extends Comparable<E>> {
// E 被擦除为Comprable
}
class Algorithm<T extends Comparable<T>> {
    public T findMaxVal(T[] array) {
        T maxVal = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(maxVal) > 0) {
                maxVal = array[i];
            }
        }
        return maxVal;
    }
}
public static void main(String[] args) {
    Algorithm<Integer> algorithm = new Algorithm<>();
    Integer[] array = {10,3,1,67,32,45};
    Integer ret = algorithm.findMaxVal(array);
    System.out.println(ret);
}
7 静态方法中的泛型

静态方法中需要在static后指定泛型, 而在类名Algorithm后面增加泛型指示对静态方法来说是没有作用的,因为static方法中没有类Algorithm的this引用。通过实参的类型可以推导出形参的T是什么类型。

class Algorithm {
    //通过实参的类型可以推导出形参的T是什么类型!
    public static<T extends Comparable<T>> T findMaxVal(T[] array) {
        T maxVal = array[0];
        for (int i = 1; i < array.length; i++) {
            if(array[i].compareTo(maxVal) > 0) {
                maxVal = array[i];
            }
        }
        return maxVal;
    }
}

public static void main(String[] args) {
    Integer[] array = {10,3,1,67,32,45};
    //通过实参的类型,确定泛型的类型
    Integer ret = Algorithm.findMaxVal(array);
	//也可以通过这种方法指定
    //Integer ret = Algorithm2.<Integer>findMaxVal(array);
    System.out.println(ret);
}
8 通配符?
  • ? 用于在泛型的使用,即为通配符
  • 相较于泛型来说,通配符不能修改,只能读。

类型通配符一般是使用? 代替具体的类型参数。例如List<?> 在逻辑上是
List,List 等所有List<具体类型实参>的父类。

通配符的上界<? extends 上界> ?必须是上界的子类

通配符的下界: <? super 下界> ?必须是 下界的父类

泛型的限制

  1. 泛型类型参数不支持基本数据类型
  2. 无法实例化泛型类型的对象
  3. 无法使用泛型类型声明静态的属性
  4. 无法使用instanceof 判断带类型参数的泛型类型
  5. 无法创建泛型类数组
  6. 无法create、catch、throw 一个泛型类异常(异常不支持泛型)
  7. 泛型类型不是形参一部分,无法重载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值