1、晓之以理,动之以码

学Java就是很上头哦,一来直接三连问!!!

什么是泛型?为什么要用泛型?泛型怎么用?

当然泛型在Java中有很重要的一个地位,在面向对象编程以及在各种设计模式中有非常广泛的应用~

1.1 什么是泛型?

泛型:
本意就是“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

1.2  一个栗子

一个被举烂的例子

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:31
* @Description TODO
*/
public class TestTypeMain {
   private static int add(int a, int b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    private static float add(float a, float b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    private static double add(double a, double b) {
       System.out.println(a + "+" + b + "=" + (a + b));
       return a + b;
  }    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();
  }    public static void main(String[] args) {
       System.out.println(TestTypeMain.add(1, 2));
       System.out.println(TestTypeMain.add(1f, 2f));
       System.out.println(TestTypeMain.add(1d, 2d));
       System.out.println(TestTypeMain.add(Integer.valueOf(1), Integer.valueOf(2)));
       System.out.println(TestTypeMain.add(Float.valueOf(1), Float.valueOf(2)));
       System.out.println(TestTypeMain.add(Double.valueOf(1), Double.valueOf(2)));
  } }
运行结果:
   1+2=3
   3
   1.0+2.0=3.0
   3.0
   1.0+2.0=3.0
   3.0
   1+2=3.0
   3.0
   1.0+2.0=3.0
   3.0
   1.0+2.0=3.0
   3.0

1.3 总结(意义):

1:减少代码,增强可读性,代码复用性更高

2:显性指定类型,编译器会自动检查,避免运行中的一半的错误。

2、泛型如何使用?

2.1 泛型可以用到哪里?

1、泛型方法:public static < E > void printArray( E[] inputArray )

public static <T extends Comparable<T>> T maximum(T x, T y, T z)

2、泛型类:public class Box<T> {  private T t; }

3、泛型接口:public interface Generator<T> { public T next(); }

4、泛型通配符:List<String> list= new ArrayList<String>();

2.2 泛型类型的变量

E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)

2.3 泛型方法

简单的泛型方法入门可以参考上面的那个栗子. 另外通过一个例子,把泛型方法再总结一下。

   /**
    * 这才是一个真正的泛型方法。
    * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T可以出现在这个泛型方法的任意位置.
    * 泛型的数量也可以为任意多个
    *   如:public <T,K> K showKeyName(Generic<T> container){
    *       ...
    *       }
    */
   public <T> T showKeyName(Generic<T> container){
       System.out.println("container key :" + container.getKey());
       //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
       T test = container.getKey();
       return test;
  }    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
   public void showKeyValue1(Generic<Number> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
   //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
   public void showKeyValue2(Generic<?> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }     /**
    * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
    * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
    * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
   public <T> T showKeyName(Generic<E> container){
       ...
   }  
   */    /**
    * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
    * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
    * 所以这也不是一个正确的泛型方法声明。
   public void showkey(T genericObj){    }
   */

此外:

   /**
    * 这才是一个真正的泛型方法。
    * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
    * 这个T可以出现在这个泛型方法的任意位置.
    * 泛型的数量也可以为任意多个
    *   如:public <T,K> K showKeyName(Generic<T> container){
    *       ...
    *       }
    */
   public <T> T showKeyName(Generic<T> container){
       System.out.println("container key :" + container.getKey());
       //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
       T test = container.getKey();
       return test;
  }    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
   public void showKeyValue1(Generic<Number> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
   //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
   public void showKeyValue2(Generic<?> obj){
       Log.d("泛型测试","key value is " + obj.getKey());
  }     /**
    * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
    * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
    * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
   public <T> T showKeyName(Generic<E> container){
       ...
   }  
   */    /**
    * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
    * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
    * 所以这也不是一个正确的泛型方法声明。
   public void showkey(T genericObj){    }
   */

2.4 泛型类

定义一个泛型类:public class GenericClass{}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:49
* @Description 泛型类
*/
public class GenericClass<T> {
   private T data;    public T getData() {
       return data;
  }    public void setData(T data) {
       this.data = data;
  }    public static void main(String[] args) {
       GenericClass<String> genericClass = new GenericClass<>();
       genericClass.setData("Generic Class");
       System.out.println(genericClass.getData());
  }
}

2.5 泛型接口

定义一个泛型接口:public interface GenericIntercace{}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:50
* @Description TODO
*/
public interface GenericInterface<T> {
  public T getData();
}

实现泛型接口不指定类型方式一:

public class ImplGenericInterface1 implements GenericIntercace

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:53
* @Description TODO
*/
public class ImplGenericInterface1<T> implements GenericInterface<T> {
   private T data;    private void setData(T data) {
       this.data = data;
  }    @Override
   public T getData() {
       return this.data;
  }    public static void main(String[] args) {
       ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
       implGenericInterface1.setData("Generic Interface1");
       System.out.println(implGenericInterface1.getData());
  }
}

实现泛型接口指定类型方式二:

public class ImplGenericInterface2 implements GenericIntercace<String> {}

package com.example.main;

/**
* @author lin
* @version 1.0
* @date 2020/7/12 21:55
* @Description TODO
*/
public class ImplGenericInterface2 implements GenericInterface<String> {
   @Override
   public String getData() {
       return "hello world ";
  }    public static void main(String[] args) {
       ImplGenericInterface2 implGenericInterface2 = new ImplGenericInterface2();
       System.out.println(implGenericInterface2.getData());
  }
}

2.6 泛型通配符

引入通配符可以在泛型实例化时更加灵活地控制,也可以在方法中控制方法的参数。

语法如下:

泛型类名<? extends T> 或 泛型类名<? super T> 或 泛型类名<?>? extends T:表示T或T的子类

? super T:表示T或T的父类

?:表示可以是任意类型

常用的 T,E,K,V,?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

2.6.1、?表示不确定的 java 类型

static int countLegs (List<? extends Animal > animals ) {
   int retVal = 0;
   for ( Animal animal : animals )
  {
       retVal += animal.countLegs();
  }
   return retVal;
} static int countLegs1 (List< Animal > animals ){
   int retVal = 0;
   for ( Animal animal : animals )
  {
       retVal += animal.countLegs();
  }
   return retVal;
}

2.6.2、T (type) 表示具体的一个java类型

下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object

在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。

private <T> void test(List<? super T> dst, List<T> src){
   for (T t : src) {
       dst.add(t);
  }
} public static void main(String[] args) {
   List<Dog> dogs = new ArrayList<>();
   List<Animal> animals = new ArrayList<>();
   new Test3().test(animals,dogs);
}

实例:

class gent<T> {
   public void test() {
       System.out.println("gent");
  }
} class supC {
   public String toString() {
       return "supA";
  }
} public class Bc extends supC {
   String b;    public Bc( String b ) {
       this.b = b;
  }    public String toString() {
       return "subB";
  }    // 指明泛型参数必须是supC或其子类
   public void test( gent<? extends supC> o ) {
       System.out.println("Bc");
  }    public static void main( String[] args ) {
       Bc bc = new Bc("test");
       gent<Bc> oGent = new gent<Bc>();
       bc.test(oGent);    // oGent 是supC的子类对象
  }
}

3、泛型注意点

3.1 静态方法与泛型

静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。

public class StaticGenerator<T> {
  ....
  ....
   /**
    * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
    * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
    * 如:public static void show(T t){..},此时编译器会提示错误信息:
         "StaticGenerator cannot be refrenced from static context"
    */
   public static <T> void show(T t){   }
}

3.2 泛型类型继承规则

1,对于泛型参数是继承关系的泛型类之间是没有继承关系的 2,泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E> 3,泛型类的继承关系在使用中同样会受到泛型类型的影响

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型继承规则测试类
*/
public class GenericInherit<T> {
   private T data1;
   private T data2;    public T getData1() {
       return data1;
  }    public void setData1(T data1) {
       this.data1 = data1;
  }    public T getData2() {
       return data2;
  }    public void setData2(T data2) {
       this.data2 = data2;
  }    public static <V> void setData2(GenericInherit<Father> data2) {   }    public static void main(String[] args) {
//       Son 继承自 Father
       Father father = new Father();
       Son son = new Son();
       GenericInherit<Father> fatherGenericInherit = new GenericInherit<>();
       GenericInherit<Son> sonGenericInherit = new GenericInherit<>();
       SubGenericInherit<Father> fatherSubGenericInherit = new SubGenericInherit<>();
       SubGenericInherit<Son> sonSubGenericInherit = new SubGenericInherit<>();        /**
        * 对于传递的泛型类型是继承关系的泛型类之间是没有继承关系的
        * GenericInherit<Father> 与GenericInherit<Son> 没有继承关系
        * Incompatible types.
        */
       father = new Son();
//       fatherGenericInherit=new GenericInherit<Son>();        /**
        * 泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E>
        */
       fatherGenericInherit=new SubGenericInherit<Father>();        /**
        *泛型类的继承关系在使用中同样会受到泛型类型的影响
        */
       setData2(fatherGenericInherit);
//       setData2(sonGenericInherit);
       setData2(fatherSubGenericInherit);
//       setData2(sonSubGenericInherit);   }    private static class SubGenericInherit<T> extends GenericInherit<T> {   }

3.3 虚拟机是如何实现泛型的

Java泛型是Java1.5之后才引入的,为了向下兼容。Java采用了C++完全不同的实现思想。Java中的泛型更多的看起来像是编译期用的 Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object.

GenericClass<String> stringGenericClass=new GenericClass<>();
GenericClass<Integer> integerGenericClass=new GenericClass<>();

C++中GenericClass<String>和GenericClass<Integer>是两个不同的类型 Java进行了类型擦除之后统一改为GenericClass<Object>

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description:泛型原理测试类
*/
public class GenericTheory {
   public static void main(String[] args) {
       Map<String, String> map = new HashMap<>();
       map.put("Key", "Value");
       System.out.println(map.get("Key"));
       GenericClass<String, String> genericClass = new GenericClass<>();
       genericClass.put("Key", "Value");
       System.out.println(genericClass.get("Key"));
  }    public static class GenericClass<K, V> {
       private K key;
       private V value;        public void put(K key, V value) {
           this.key = key;
           this.value = value;
      }        public V get(V key) {
           return value;
      }
  }    /**
    * 类型擦除后GenericClass2<Object>
    * @param <T>
    */
   private class GenericClass2<T> {   }    /**
    * 类型擦除后GenericClass3<ArrayList>
    * 当使用到Serializable时会将相应代码强制转换为Serializable
    * @param <T>
    */
   private class GenericClass3<T extends ArrayList & Serializable> {   }
}

对应的字节码文件

public static void main(String[] args) {
       Map<String, String> map = new HashMap();
       map.put("Key", "Value");
       System.out.println((String)map.get("Key"));
       GenericTheory.GenericClass<String, String> genericClass = new GenericTheory.GenericClass();
       genericClass.put("Key", "Value");
       System.out.println((String)genericClass.get("Key"));
  }

4.学以致用

1,泛型解析JSON数据封装

api返回的json数据

{
   "code":200,
   "msg":"成功",
   "data":{
       "name":"Lin",
       "email":"10086"
  }
}
BaseResponse .java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 接口数据接收基类
*/
public class BaseResponse {    private int code;
   private String msg;    public int getCode() {
       return code;
  }    public void setCode(int code) {
       this.code = code;
  }    public String getMsg() {
       return msg;
  }    public void setMsg(String msg) {
       this.msg = msg;
  }
}
UserResponse.java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 用户信息接口实体类
*/
public class UserResponse<T> extends BaseResponse {
   private T data;    public T getData() {
       return data;
  }    public void setData(T data) {
       this.data = data;
  }
}

2,泛型+反射实现巧复用工具类

/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型相关的工具类
*/
public class GenericUtils {    public static class Movie {
       private String name;
       private Date time;        public String getName() {
           return name;
      }        public Date getTime() {
           return time;
      }        public Movie(String name, Date time) {
           this.name = name;
           this.time = time;
      }        @Override
       public String toString() {
           return "Movie{" + "name='" + name + '\'' + ", time=" + time + '}';
      }
  }    public static void main(String[] args) {
       List<Movie> movieList = new ArrayList<>();
       for (int i = 0; i < 5; i++) {
           movieList.add(new Movie("movie" + i, new Date()));
      }
       System.out.println("排序前:" + movieList.toString());        GenericUtils.sortAnyList(movieList, "name", true);
       System.out.println("按name正序排:" + movieList.toString());        GenericUtils.sortAnyList(movieList, "name", false);
       System.out.println("按name逆序排:" + movieList.toString());
  }    /**
    * 对任意集合的排序方法
    * @param targetList 要排序的实体类List集合
    * @param sortField 排序字段
    * @param sortMode   true正序,false逆序
    */
   public static <T> void sortAnyList(List<T> targetList, final String sortField, final boolean sortMode) {
       if (targetList == null || targetList.size() < 2 || sortField == null || sortField.length() == 0) {
           return;
      }
       Collections.sort(targetList, new Comparator<Object>() {
           @Override
           public int compare(Object obj1, Object obj2) {
               int retVal = 0;
               try {
                   // 获取getXxx()方法名称
                   String methodStr = "get" + sortField.substring(0, 1).toUpperCase() + sortField.substring(1);
                   Method method1 = ((T) obj1).getClass().getMethod(methodStr, null);
                   Method method2 = ((T) obj2).getClass().getMethod(methodStr, null);
                   if (sortMode) {
                       retVal = method1.invoke(((T) obj1), null).toString().compareTo(method2.invoke(((T) obj2), null).toString());
                  } else {
                       retVal = method2.invoke(((T) obj2), null).toString().compareTo(method1.invoke(((T) obj1), null).toString());
                  }
              } catch (Exception e) {
                   System.out.println("List<" + ((T) obj1).getClass().getName() + ">排序异常!");
                   e.printStackTrace();
              }
               return retVal;
          }
      });
  }
}

3,Gson库中的泛型的使用-TypeToken

/**
* Author:Jay On 2019/5/11 22:11
* <p>
* Description: Gson库中的泛型使用
*/
public class GsonGeneric {
   public static class Person {
       private String name;
       private int age;        public Person(String name, int age) {
           this.name = name;
           this.age = age;
      }        @Override
       public String toString() {
           return "Person{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
      }
  }    public static void main(String[] args) {
       Gson gson = new Gson();
       List<Person> personList = new ArrayList<>();
       for (int i = 0; i < 5; i++) {
           personList.add(new Person("name" + i, 18 + i));
      }
       // Serialization
       String json = gson.toJson(personList);
       System.out.println(json);
       // Deserialization
       Type personType = new TypeToken<List<Person>>() {}.getType();
       List<Person> personList2 = gson.fromJson(json, personType);
       System.out.println(personList2);
  }
}

发哥讲

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注公众号

20、Java 泛型的更多相关文章

  1. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...

  2. 18.Java泛型

    1.为什么需要泛型 List list1=new ArrayList(Arrays.asList(new String("string"),new Integer(20))); S ...

  3. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  4. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  5. 全面总结Java泛型

    本文对Java泛型进行了全面的总结.文章内容包括普通泛型.通配符.受限泛型.泛型接口.泛型方法.返回泛型类型实例等等. 虽然Scala创始人Martin Odersky说当年正是因为Java泛型的丑陋 ...

  6. Java学习笔记(二一)——Java 泛型

    [前面的话] 最近脸好干,掉皮,需要买点化妆品了. Java泛型好好学习一下. [定义] 一.泛型的定义主要有以下两种: 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个 ...

  7. Java 泛型(Generics)

    Generics, 类似C++中的模版. 允许在定义类和接口的时候使用类型参数(type parameters), 声明的类型参数在使用的时候用具体的类型来替换. 如 ArrayList<Str ...

  8. java泛型 8 泛型的内部原理:类型擦除以及类型擦除带来的问题

    参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...

  9. Java泛型介绍!!!

    Java总结篇系列:Java泛型  转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...

随机推荐

  1. 数据分析02 /pandas基础

    数据分析02 /pandas基础 目录 数据分析02 /pandas基础 1. pandas简介 2. Series 3. DataFrame 4. 总结: 1. pandas简介 numpy能够帮助 ...

  2. 收集TCP端口的访问延迟和丢包率

    需求: 找一款工具可以对TCP 80端口 收集 访问延迟和丢包率 找到的工具: 1.Hping :  http://www.hping.org/ 2.paping : https://docs.azu ...

  3. vue中使用elmentUI的Upload组件提交文件和后台接收

    1.参考此博客,希望有以下知识储备 vue的路由,跨域请求,springboot2.X,html,已经阅读elementUI官网中关于upload组件的详细介绍. 2.废话不多说,直接讲解细节. 前台 ...

  4. 双网卡bonding

    网卡:计算机与外界局域网的连接是通过主机箱内插入一块网络接口板(或者是在笔记本电脑中插入一块PCMCIA卡).网络接口板又称为通信适配器或网络适配器(adapter)或网络接口卡NIC(Network ...

  5. bzoj4318OSU!*

    bzoj4318OSU! 题意: 一个长度为n的序列,每个元素有一定概率是1,不是1就是0.连续x个1可以贡献x^3的分数,问期望分数. 题解: 期望dp.f1[i]表示连续到i的期望长度,f2[i] ...

  6. Mesos+Zookeeper+Marathon+Docker环境搭建

    相关理论请参考:https://www.cnblogs.com/Bourbon-tian/p/7155054.html,本文基于https://www.cnblogs.com/Bourbon-tian ...

  7. [jvm] -- 常用内存参数配置篇

    新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ) Eden : from : to = 8 : 1 : 1 ( 可 ...

  8. “Python的单例模式有四种写法,你知道么?”——孔乙己

    什么是单例模式 单例模式(Singleton Pattern)是最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创建自己的 ...

  9. 乌班图16 配置nginx

    阿里云 乌班图16 安装ngnix sudo apt install nginx nginx 启动 重启 关闭 sudo service nginx start restart stop status ...

  10. 7.20试机测 T3 阶乘之和 暴力AC题解

    7.20试机测  T3 阶乘之和 暴力AC题解 题外话:此乃本蒟蒻发表的第一篇题解,大家多多关照,支持一下,谢谢 题面 3.阶乘之和(sum.pas/in/out) 问题描述: 给定一个非负整数 n, ...