飞道的博客

泛型(工作能用到的)

328人阅读  评论(0)

什么是泛型

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 对象,地址不同


转载:https://blog.csdn.net/weixin_61427900/article/details/125505139
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场