• 泛型(工作能用到的)


    什么是泛型

    Java编程思想》对 泛型的介绍:一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

    因此,泛型:就是适用于多种类型。从代码上讲,就是对类型实现了参数化。

    语法

    class 泛型类名称 < 类型形参列表 > {
    // 这里可以使用类型参数
    }
    class ClassName < T1 , T2 , ..., Tn > {
    }

    例如:我们要实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值

    1. class MyArray2<T>{
    2. public T[] array=(T[])new Object[10];
    3. public T getValue(int pos){
    4. return array[pos];
    5. }
    6. public void setValue(int pos, T val){
    7. array[pos]=val;
    8. }
    9. }
    10. public class Test2 {
    11. public static void main(String[] args) {
    12. //<>里面指定了类型,说明此时这个类里面只能放这个数据类型的数据
    13. //并且<>中是引用类型
    14. MyArray2<String> myArray2=new MyArray2<String>();
    15. myArray2.setValue(0,"hello");
    16. myArray2.setValue(1,"world");
    17. System.out.println(myArray2.getValue(0));
    18. System.out.println(myArray2.getValue(1));
    19. MyArray2<Integer> myArray3=new MyArray2<Integer>();
    20. myArray3.setValue(2,100);
    21. System.out.println(myArray3.getValue(2));
    22. }
    23. }

    1.<>当中指定类型后,编译器会根据你指定的类型参数来进行类型的检查

    2.取元素时不用进行强转

    T[]array=new T[10]//error是错误的 

     泛型的使用

    语法

    泛型类 < 类型实参 > 变量名 ; // 定义一个泛型类引用
    new 泛型类 < 类型实参 > ( 构造方法实参 ); // 实例化一个泛型类对象

     例如上面代码:MyArray2<String> myArray2=new MyArray2<Stirng>();//后面<>中的类型可写可不写,编译器可以根据上下文推导出类型实参。

    泛型如何编译

    擦除机制

     我们通过字节码发现在编译时,所有的T都变成了Object.

     所以擦除机制就是在 编译的时候 把泛型T 擦除成了Object.运行期间没有这个泛型这个概念。

    思考:既然T都被擦除成了Object了,为什么不能实例化泛型类型的数组?

    T[]array=new T[10]//等价于Object[]array=new Object[10];  错误

    1. class MyArray2<T>{
    2. public T[] array=(T[])new Object[10];
    3. public T getValue(int pos){
    4. return array[pos];
    5. }
    6. public void setValue(int pos, T val){
    7. array[pos]=val;
    8. }
    9. public T[] getArray(){
    10. return array;
    11. }
    12. }
    13. public class Test2 {
    14. public static void main(String[] args) {
    15. MyArray2 myArray2=new MyArray2();
    16. myArray2.setValue(0,10);
    17. myArray2.setValue(1,"hello");
    18. Integer[] integers= (Integer[]) myArray2.getArray();
    19. System.out.println(Arrays.toString(integers));
    20. }
    21. }

      因为返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Intefer类型的数组,编译器认为是不安全的。

    正确的创建方法(了解即可)

    1. class MyArray3<T>{
    2. T[] array;
    3. /**
    4. * 通过放射创建,指定类型的数组
    5. * @param clazz
    6. * @param capacity
    7. */
    8. public MyArray3(Class<T>clazz,int capacity){
    9. array=(T[]) Array.newInstance(clazz,capacity);
    10. }
    11. public T getPos(int pos){
    12. return array[pos];
    13. }
    14. public void setArray(int pos,T val){
    15. array[pos]=val;
    16. }
    17. public T[] getArray(){
    18. return array;
    19. }
    20. }
    21. public class Test3 {
    22. public static void main(String[] args) {
    23. MyArray3<Integer> myArray3=new MyArray3<>(Integer.class,10);
    24. myArray3.setArray(0,10);
    25. myArray3.setArray(1,15);
    26. Integer[] integers=myArray3.getArray();
    27. System.out.println(Arrays.toString(integers));
    28. }
    29. }

    泛型的边界 

    有上边界,无下边界。

    语法

    class 泛型类名称 < 类型形参 extends 类型边界 > {
    ...
    }

     例如:

    public class MyArray < E extends Number > {
    ...
    }

    这里的extends不是继承的意思,表示边界是Number及其子类 

    应用:实现一个泛型类,找出数组最大值

    因为泛型类T不是基本类型,不能简单的进行大于小于这样的比较,T是引用类型要使用比较器,也就是将T擦除成Comparable接口。

    1. class Alg<T extends Comparable<T>>{//将T擦成Comparable接口
    2. public T findMax(T[] array){
    3. T max=array[0];
    4. for (int i = 0; i < array.length; i++) {
    5. if(max.compareTo(array[i])<0){
    6. max=array[i];
    7. }
    8. }
    9. return max;
    10. }
    11. }

    泛型方法

    1.成员方法:方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

    1. class Alg{
    2. public<T extends Comparable<T>> T findMax(T[] array){
    3. T max=array[0];
    4. for (int i = 0; i < array.length; i++) {
    5. if(max.compareTo(array[i])<0){
    6. max=array[i];
    7. }
    8. }
    9. return max;
    10. }
    11. }
    12. public class Test {
    13. public static void main(String[] args) {
    14. Alg alg=new Alg();
    15. Integer[] array=new Integer[]{1,23,4,56,7,8};
    16. System.out.println(alg.findMax(array));
    17. }
    18. }

    56 

    2.静态方法,静态的泛型方法 需要在static后用<>声明泛型类型参数

    1. class Alg2{
    2. //静态方法不依赖于对象的调用
    3. public static<T extends Comparable<T>> T findMax(T[] array){
    4. T max=array[0];
    5. for (int i = 0; i < array.length; i++) {
    6. if(max.compareTo(array[i])<0){
    7. max=array[i];
    8. }
    9. }
    10. return max;
    11. }
    12. }

    通配符

    ? 用于在泛型的使用,即为通配符
    通配符是用来解决泛型无法协变的问题的 ,协变指的就是如果 Student Person 的子类,那么 List<Student> 也应该是 List<Person> 的子类。但是泛型是不支持这样的父子类关系的。
    泛型 T 是确定的类型,一旦你传了我就定下来了,而通配符则更为灵活或者说是不确定,更多的是用于扩充参数的范围。
    举个例子
    1. class Message<T>{
    2. private T message;
    3. public T getMessage() {
    4. return message;
    5. }
    6. public void setMessage(T message) {
    7. this.message = message;
    8. }
    9. }
    10. public class Test2 {
    11. public static void func(Message<?> message){
    12. //message.setMessage(100);//不能修改,因为不知道参数是什么类型的
    13. System.out.println(message.getMessage());
    14. }
    15. public static void main(String[] args) {
    16. Message<String> message=new Message<>();
    17. String s="中国世界第一";
    18. message.setMessage(s);
    19. func(message);
    20. Message<Integer> message2=new Message<>();
    21. Integer a=10;
    22. message2.setMessage(a);
    23. func(message2);
    24. }
    25. }

    通过使用通配符,既可以传String也可以传Integer,扩大了参数的范围

    通配符的上界(常用于获取元素)

    语法

    <? extends 上界 >
    <? extends Number > // 可以传入的实参类型是 Number 或者 Number 的子类
    应用:
    1. class Food{
    2. }
    3. class Fruit extends Food{
    4. }
    5. class Apple extends Fruit{
    6. }
    7. class Banana extends Fruit{
    8. }
    9. class Alg2<T>{
    10. private T val;
    11. public T getVal() {
    12. return val;
    13. }
    14. public void setVal(T val) {
    15. this.val = val;
    16. }
    17. }
    18. public class Test3 {
    19. /**
    20. * ?通配符extends上限是Fruit及其子类
    21. * @param temp
    22. */
    23. public static void func(Alg2<? extends Fruit> temp){
    24. /*
    25. Banana banana=temp.getVal();
    26. Apple apple=temp.getVal();
    27. 不能进行修改,因为temp接收的是Fruit及其子类,无法确定参数是什么类型
    28. */
    29. Fruit fruit= temp.getVal();//temp放的是Fruit及其子类,所以可以用Fruit进行接收
    30. System.out.println(temp.getVal());
    31. }
    32. public static void main(String[] args) {
    33. Alg2<Banana> alg2=new Alg2<>();
    34. alg2.setVal(new Banana());
    35. func(alg2);
    36. Alg2<Apple> alg3=new Alg2<>();
    37. alg3.setVal(new Apple());
    38. func(alg3);
    39. }
    40. }

    通配符的下限(常用于设置元素)

    语法

    <? super 下界 >
    <? super Integer > // 代表 可以传入的实参的类型是 Integer 或者 Integer 的父类类型

    应用:

    1. class Food{
    2. }
    3. class Fruit extends Food {
    4. }
    5. class Apple extends Fruit {
    6. }
    7. class Banana extends Fruit {
    8. }
    9. class Alg4<T>{
    10. private T val;
    11. public T getVal() {
    12. return val;
    13. }
    14. public void setVal(T val) {
    15. this.val = val;
    16. }
    17. }
    18. public class Test4 {
    19. /**
    20. * 通配符?下限super是Fruit及其父类
    21. * @param temp
    22. */
    23. public static void func(Alg4<? super Fruit> temp){
    24. /**
    25. * 因为最小的下限是Fruit,所以可以使用setVal进行修改
    26. */
    27. temp.setVal(new Banana());//向上转型
    28. temp.setVal(new Apple());//向上转型
    29. /**
    30. * temp可能是Fruit或者他的父类,无法用子类(Fruit)接收
    31. */
    32. //Fruit fruit=temp.getVal();
    33. System.out.println(temp.getVal());
    34. }
    35. public static void main(String[] args) {
    36. Alg4<Fruit> alg4=new Alg4<>();
    37. alg4.setVal(new Fruit());
    38. Alg4<Food> alg41=new Alg4<>();
    39. alg41.setVal(new Food());
    40. }
    41. }

    (自动)装箱

    1. public static void main(String[] args) {
    2. int a=10;
    3. Integer integer1=a;//自动装箱
    4. Integer integer2=new Integer(a);//显示装箱
    5. Integer integer3=Integer.valueOf(a);//显示装箱
    6. }

    通过字节码我们发现,装箱的底层是采用Integer.valueOf

    (自动)拆箱

    1. public static void main(String[] args) {
    2. Integer integer=10;
    3. int val=integer;//自动拆箱
    4. System.out.println(val);
    5. int val2=integer.intValue();//显示拆箱
    6. double val3=integer.doubleValue();//显示拆箱
    7. }

    面试题:输出什么

    1. public static void main(String[] args) {
    2. Integer a=127;
    3. Integer b=127;
    4. System.out.println(a==b);
    5. Integer c=128;
    6. Integer d=128;
    7. System.out.println(c==d);
    8. }

    答案:

    true

    false 

    why?

     装箱的底层是valueOf,查看他的源码发现在【-127,128】范围内有一个数组,不在这个范围内需要new 对象,所以127在数组内相同,128需要new 对象,地址不同

  • 相关阅读:
    江南爱窗帘十大品牌 | 主卧用什么窗帘最好 主卧窗帘颜色搭配有技巧
    多目标应用:基于非支配排序粒子群优化算法NSPSO求解无人机三维路径规划(MATLAB代码)
    146.LRU缓存
    HCIA 笔记(1)
    DOM-1
    NFT Insider #72:The Sandbox 阿尔法第三季正式启动,耐克NFT已产生超过1.85亿美元的收入
    windows+Reids可视化工具RESP.app的安装以及使用
    【C++编程语言】之引用
    互联网曝光文明城市监督举报小程序开发
    架构师,别再扯淡了!
  • 原文地址:https://blog.csdn.net/weixin_61427900/article/details/125505139