前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java泛型详解

java泛型详解

作者头像
码农王同学
发布2020-04-08 18:03:21
1.4K0
发布2020-04-08 18:03:21
举报
文章被收录于专栏:后端Coder后端Coder
这次写一篇关于java泛型的文章,java泛型说难也不难,说容易也不容易,泛型的出现主要是为了帮助我们在编码时在编译时期就可以预先检查可能出现的错误类型转换问题。

1,泛型的定义以及存在的意义。

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

例如:GenericClass<T>{}

代码语言:javascript
复制
一些常用的泛型类型变量:
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)

如果要实现不同类型的加法,每种类型都需要重载一个add方法。可以发现用了泛型,我们只需要写一个add方法就可以了

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

/**
 * 为什么使用泛型?
 *
 * @author wpw
 */
public class NeedGeneric1 {
    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();
    }

    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;
    }

    public static void main(String[] args) {
        NeedGeneric1.add(1, 2);
        NeedGeneric1.add(1f, 2f);
        NeedGeneric1.add(1d, 2d);
        NeedGeneric1.add(Integer.valueOf(1), Integer.valueOf(2));
        NeedGeneric1.add(Float.valueOf(1), Float.valueOf(2));
        NeedGeneric1.add(Double.valueOf(1), Double.valueOf(2));
    }
}

去除集合元素时需要人为的强制类型转换到具体的目标类型,且很容易出现"java.lang.ClassCastException"异常。

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;

/**
 * 为什么要使用泛型?
 *
 * @author wpw
 */
public class NeedGeneric2 {
    @NoArgsConstructor
    @Builder
    @Accessors(chain = true)
    @Data
    static class C {
    }

    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add(new C());
        list.add(100);
        /**
         * 当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型
         * 变成了Object类型,但其运行时类型任然为其本身类型。
         * 因此,//1出取出集合元素时需要人为的强制类型转换到具体的目标类型,且很容易出现"java.lang.ClassCastException"异常。
         *
         */
        for (int i = 0, size = list.size(); i < size; i++) {
            System.out.println(list.get(i));
            String value = (String) list.get(i);
            System.out.println(value);
        }
    }
}

上面由于我们没有进行泛型的设置,当使用String类型接收数据时就会出现类型转换异常。

代码语言:javascript
复制
Exception in thread "main" java.lang.ClassCastException: com.wpw.springbootjuc.generic.NeedGeneric2$C cannot be cast to java.lang.String
  at com.wpw.springbootjuc.generic.NeedGeneric2.main(NeedGeneric2.java:39)

所以这里就体现了使用泛型的意义。

代码语言:javascript
复制
1,适用于多种数据类型执行相同的代码--代码复用
2,泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

关于泛型的使用,一般是泛型类,泛型接口,泛型方法的使用上,所以接下来我们按照上面的顺序进行定义和使用。

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

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 泛型类的使用
 *
 * @author wpw
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(chain = true)
public class GenericClass<T> {
    private T data;

    public static void main(String[] args) {
        GenericClass<String> genericClass = new GenericClass<>();
        genericClass.setData("Generic Class");
        System.out.println(genericClass.getData());
    }
}

3,泛型接口的使用

定义一个泛型接口:public interfaceGenericInterface<T>{}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

/**
 * 泛型接口
 *
 * @author wpw
 */
public interface GenericInterface<T> {
    /**
     * 获取数据
     *
     * @return 数据 t
     */
    T getData();
}

实现泛型接口方式一:

public class GenericInterfaceImpl<T> implements GenericInterface<T>{}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.Getter;
import lombok.Setter;

/**
 * 泛型接口实现类-泛型类实现方式
 *
 * @author wpw
 */
public class GenericInterfaceImpl1<T> implements GenericInterface<T> {
    @Getter
    @Setter
    private T data;

    @Override
    public T getData() {
        return data;
    }

    public static void main(String[] args) {
        GenericInterfaceImpl1 genericInterfaceImpl1 = new GenericInterfaceImpl1();
        genericInterfaceImpl1.setData("Generic Interface1");
        System.out.println(genericInterfaceImpl1.getData());
    }
}

实现泛型接口方式二:

public class GenericInterfaceImpl2 implements GenericInterface<String>{}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

/**
 * 泛型接口实现类-指定具体类型实现方式
 *
 * @author wpw
 */
public class GenericInterfaceImpl2 implements GenericInterface<String> {
    @Override
    public String getData() {
        return "Generic Interface2";
    }

    public static void main(String[] args) {
        GenericInterfaceImpl2 genericInterfaceImpl2 = new GenericInterfaceImpl2();
        System.out.println(genericInterfaceImpl2.getData());
    }
}

4,泛型方法的使用

定义一个泛型方法:private static<T> T genericAdd(T a,T b){}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

/**
 * 泛型方法
 *
 * @author wpw
 */
public class GenericMethod1 {
    private static int add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static <T> T genericAdd(T a, T b) {
        System.out.println(a + "+" + b + "=" + a + b);
        return a;
    }

    public static void main(String[] args) {
        GenericMethod1.add(1, 2);
        String str = GenericMethod1.genericAdd("a", "b");
        System.out.println("str = " + str);
    }
}

和文章最开始说的一样,泛型的作用就是为了更加精确的保证在编译时期类型是安全的。

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

/**
 * 泛型方法
 *
 * @author wpw
 */
public class GenericMethod3 {
    static class Animal {
        @Override
        public String toString() {
            return "Animal";
        }
    }

    static class Dog extends Animal {
        @Override
        public String toString() {
            return "Dog";
        }
    }

    static class Fruit {
        @Override
        public String toString() {
            return "Fruit";
        }
    }

    static class GenericClass<T> {
        public void show01(T t) {
            System.out.println(t.toString());
        }

        public void show02(T t) {
            System.out.println(t.toString());
        }

        public void show03(T t) {
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();
        Fruit fruit = new Fruit();
        GenericClass<Animal> genericClass = new GenericClass<>();
        //泛型类在初始化时限制了参数类型
        genericClass.show01(dog);
//        genericClass.show01(fruit);
        //泛型方法的参数类型在使用时指定
        genericClass.show02(dog);
//        genericClass.show02(fruit);
        genericClass.<Animal>show03(animal);
        genericClass.<Animal>show03(dog);
//        genericClass.show03(fruit);
        genericClass.<Dog>show03(animal);
    }
}

5,限定泛型类型变量

1,对方法的限定

public static <T extends Comparable<T>> T getMin(T a,T b){}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import java.io.Serializable;

/**
 * 类型变量的限定-方法
 *
 * @author wpw
 */
public class TypeLimitForMethod {
    /**
     * 计算最小值,如果要实现这样的功能就需要对泛型方法的类型做出限定
     *
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    private static <T extends Comparable> T getMin(T a, T b) {
        return (a.compareTo(b) > 0) ? a : b;
    }

    /**
     * 限定类型使用extends关键字指定,可以使类,接口,类放在前面,接口放在后面用&符号分隔
     *
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    public static <T extends Comparable & Serializable> T getMinBySerializable(T a, T b) {
        return a.compareTo(b) < 0 ? a : b;
    }

    public static void main(String[] args) {
        System.out.println(TypeLimitForMethod.getMin(2, 4));
        System.out.println(TypeLimitForMethod.getMinBySerializable("a", "r"));
        System.out.println("a".hashCode());
        System.out.println("b".hashCode());
    }
}

2,对类的限定

public class TypeLimitForClass<T extends List & Serializable>{}

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.Data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 类型变量的限定-类
 *
 * @author wpw
 */
@Data
public class TypeLimitForClass<T extends List & Serializable> {
    private T data;

    public static void main(String[] args) {
        ArrayList<String> stringArrayList = new ArrayList<>();
        stringArrayList.add("A");
        stringArrayList.add("B");
        ArrayList<Integer> integerArrayList = new ArrayList<>();
        integerArrayList.add(1);
        integerArrayList.add(2);
        integerArrayList.add(3);
        TypeLimitForClass<ArrayList> typeLimitForClass01 = new TypeLimitForClass<>();
        typeLimitForClass01.setData(stringArrayList);
        TypeLimitForClass<ArrayList> typeLimitForClass02 = new TypeLimitForClass<>();
        typeLimitForClass02.setData(integerArrayList);
        System.out.println(getMinListSize(typeLimitForClass01.getData().size(), typeLimitForClass02.getData().size()));
    }

    private static <T extends Comparable<T> & Serializable> T getMinListSize(T a, T b) {
        return a.compareTo(b) < 0 ? a : b;
    }
}

学以致用,接下来看下如何在实际的场景中如何使用泛型。

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.Data;

/**
 * 接口数据接收基类
 * @author wpw
 */
@Data
public class BaseResponse {
    private int code;
    private String msg;
}
代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 用户基础类
 *
 * @author wpw
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(chain = true)
public class User<T extends UserResponse & Serializable> implements Serializable {
    private String name;
    private String email;
}
代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import com.google.gson.Gson;
import lombok.Data;

/**
 * 用户信息接口实体类
 *
 * @author wpw
 */
@Data
public class UserResponse<T> extends BaseResponse {
    private T data;

    public static void main(String[] args) {
        UserResponse userResponse=new UserResponse();
        userResponse.setData(new User<>().setName("Jay").setEmail("10086"));
        userResponse.setCode(200);
        userResponse.setMsg("成功");
        Gson gson=new Gson();
        String json = gson.toJson(userResponse);
        System.out.println("json = " + json);
    }
}

输出的内容信息如下

代码语言:javascript
复制
json = {"data":{"name":"Jay","email":"10086"},"code":200,"msg":"成功"}

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

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 泛型相关的工具类
 *
 * @author wpw
 */
public class GenericUtils {
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @Accessors(chain = true)
    public static class Movie {
        private String name;
        private LocalDateTime 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, LocalDateTime.now()));
        }
        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逆序
     * @param <T>
     */
    public static <T> void sortAnyList(List<T> targetList, final String sortField, final boolean sortMode) {
        if (targetList == null || CollectionUtils.isEmpty(targetList) || targetList.size() < 2
                || sortField == null || sortField.length() == 0) {
            return;
        }
        Collections.sort(targetList, (o1, o2) -> {
            int retVal = 0;
            try {
                String methodStr = "get" + sortField.substring(0, 1).toUpperCase() + sortField.substring(1);
                Method method1 = o1.getClass().getMethod(methodStr, null);
                Method method2 = o2.getClass().getMethod(methodStr, null);
                if (sortMode) {
                    retVal = method1.invoke(o1, null).toString().compareTo(method2.invoke(o2, null).toString());
                } else {
                    retVal = method2.invoke(o2, null).toString().compareTo(method1.invoke(o1, null).toString());
                }
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                System.out.println("List<" + o1.getClass().getName() + ">排序异常");
                e.printStackTrace();
            }
            return retVal;
        });
    }
}

Gson库中的泛型的使用-TypeToken

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * Gson库中的泛型使用
 *
 * @author wpw
 */
public class GsonGeneric {
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @Accessors(chain = true)
    public static class Person {
        private String name;
        private int age;
    }

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            personList.add(new Person("name" + i, 18 + i));
        }
        //Serialization
        Gson gson = new Gson();
        String json = gson.toJson(personList);
        System.out.println("json = " + json);
        //Deserialization
        Type personType = new TypeToken<List<Person>>() {
        }.getType();
        List<Person> personList2 = gson.fromJson(json, personType);
        System.out.println("personList2 = " + personList2);
    }
}

最后写个工具类简单使用一下

代码语言:javascript
复制
package com.wpw.springbootjuc.generic;

import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 工具类
 *
 * @author wpw
 */
public class GenericUtil<T> {
    public static <T extends Number & Serializable> Set<T> getList(List<T> list) {
        return new HashSet<>(list);
    }

    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(Student.builder().build().setName("公众号WwpwW").setId(1));
        studentList.add(Student.builder().build().setName("公众号WwpwW2").setId(2));
        studentList.add(Student.builder().build().setName("公众号WwpwW3").setId(3));
        studentList.add(Student.builder().build().setName("公众号WwpwW4").setId(4));
        studentList.add(Student.builder().build().setName("公众号WwpwW5").setId(1));
        Set<Integer> set = getList(studentList.stream().map(Student::getId).collect(Collectors.toList()));
        set.stream().sorted(Integer::compareTo).forEach(x -> System.out.print(x + "\t"));
    }

    @Data
    @Builder
    @Accessors(chain = true)
    static class Student {
        private Integer id;
        private String name;
    }
}
代码语言:javascript
复制
文章参考www.jianshu.com/p/986f732ed2f1

java泛型的使用到这里结束了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档