侧边栏壁纸
博主头像
laasc

Coding changes the world

  • 累计撰写 7 篇文章
  • 累计创建 9 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

java泛型面试问题总结

laasc
2022-04-12 / 0 评论 / 2 点赞 / 614 阅读 / 1,864 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-08-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

为什么需要泛型?

适用于多种数据类型执行相同的代码,看下面例子

private static int put(int a, int b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static float put(float a, float b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static double put(double a, double b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

没有泛型的话,要实现不同类型的加减,每个类型都需要重载一个put方法;通过泛型,我们可以复用一个方法:

private static <T extends Number> double add(T a, T b) {
    System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
    return a.doubleValue() + b.doubleValue();
}

泛型中的类型在使用的过程去指定,不需要强制类型转换(这样类型是安全的,编译器会在编译阶段检查类型)
看下面的这个例子:

List<String> strList = new ArrayList<>();
List<Object> objList = new ArrayList<>();
objList.add("laasc"); // 代码A
objList = strList; // 代码B

代码A很明显是合法的。Object类型是String类型的父类。那么代码B为什么不合法呢?

在 Java 中,对象类型的赋值其实是引用地址的赋值,也就是说,假设代码B赋值成功,objList和strList变量引用的是同一个地址。那会有什么问题呢?

如果此时,往objList中添加了一个非String类型的元素,也就相当于往strList中添加了一个非String类型的元素。很明显,此处就破坏了List<String> strList。所以,Java 编译器会认为代码2是非法的,这是一种安全的做法。

这里我们简单的定义一个泛型类:

class PointParadigm<T> { // 随便定义一个泛型类,T是泛型标志符,T是type的简称
        private T var; // var参数的类型由T指定,外部传入时指定

        public T getVar() {  // 此时返回值的类型由外部指定
            return var;
        }

        public void setVar(T var) { // 设置的类型也是由外部指定
            this.var = var;
        }
    }

    public  class GeneratorDemo {
        public static void main(String[] args){
            PointParadigm<String> p = new PointParadigm<String>() ;     // 指定泛型类里面的var类型为String类型
            p.setVar("test") ;                            // 设置字符串
            System.out.println(p.getVar().length()) ;   // 打印字符串的长度
        }
    }

多元泛型

class User<K,V>{       // 此处指定了两个泛型类型  
  private K name ;     // 此变量的类型由外部决定  
  private V age ;   // 此变量的类型由外部决定  

  public K getName() {
      return name;
  }
  public void setName(K name) {
      this.name = name;
  }
  public V getAge() {
      return age;
  }
  public void setAge(V age) {
      this.age = age;
  }
}
public class GeneratorDemo {  
  public static void main(String args[]){  
      user<String,Integer> t = null ;    // 定义两个泛型类型的对象  
      t = new user<String,Integer>() ;   // 里面的name为String,age为Integer  
      t.setName("tom") ;        // 设置第一个内容  
      t.setAge(20) ;            // 设置第二个内容  
      System.out.print("姓名;" + t.getName()) ;      // 取得信息  
      System.out.print(",年龄;" + t.getAge()) ;       // 取得信息  

  }  
}

泛型接口如何定义和使用?

简单的泛型接口

interface UserInfo<T>{        // 在接口上定义泛型  
  public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型  
}  
class UserInfoImpl<T> implements UserInfo<T>{   // 定义泛型接口的子类  
  private T var ;             // 定义属性  
  public UserInfoImpl(T var){     // 通过构造方法设置属性内容  
      this.setVar(var) ;    
  }  
  public void setVar(T var){  
      this.var = var ;  
  }  
  public T getVar(){  
      return this.var ;  
  }  
} 
public class GeneratorDemo{  
  public static void main(String arsg[]){  
      Info<String> i = null;        // 声明接口对象  
      i = new UserInfoImpl<String>("tom") ;  // 通过子类实例化对象  
      System.out.println("内容:" + i.getVar()) ;  
  }  
}  

泛型方法如何定义和使用?

泛型方法,是在调用方法的时候指明泛型的具体类型。

public <T> T getData(Class<T> c) {
  T t =  c.newInstance();
return t;
}

调用泛型方法格式

Generator generator = new Generator();
      Object obj = generator.getData(Class.forName("这里是指定泛型的具体类型"));

定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。 Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。

为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。 泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。

当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。

泛型的上限和下限?

在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

上限

class Info<T extends Number>{    // 此处泛型只能是数字类型
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
}
public class GeneratorDemo{
    public static void main(String args[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象
    }
}

下限

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
}
public class GeneratorDemo{
    public static void main(String args[]){
        Info<String> i1 = new Info<String>() ;        // 声明String的泛型对象
        Info<Object> i2 = new Info<Object>() ;        // 声明Object的泛型对象
        i1.setVar("hello") ;
        i2.setVar(new Object()) ;
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? super String> temp){    // 只能接收String或Object类型的泛型,String类的父类只有Object类
        System.out.print(temp + ", ") ;

理解Java中的泛型是伪泛型?

泛型中类型擦除 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。

2

评论区