泛型在用法上可以分成泛型类泛型方法两种:

泛型类

泛型类在实例化时就要指明类型,不同的类型实例必须要重新new一次,不够灵活

泛型接口interface Request<T>
     * 1.接口中有方法,且引用了泛型T时,class SubRequest<T> implements Request<T>
     * 		父子都要有<T>否则不能编译,因为实现的方法中有T
     * 2.接口中没有方法,以下三种定义都可以,第三种相当字面T改成了字面M
     * 		class SubRequest<T> implements Request<T>* 		class SubRequest implements Request* 		class SubRequest<M> implements Request
     * 3.子类新增泛型
     * 		class SubRequest<K, T> implements Request<T>

泛型方法

定义泛型方法时,必须在返回值前加上<T>,声明该方法是泛型方法,泛型方法在调用时指明类型,比较灵活,如果泛型方法返回泛型类型时,方法返回时要强转为泛型类型,调用方直接用真实类型接收

// 无返回值
public static <T> void test(T t) {
	System.out.println(t.getClass());
}
// 有返回值
User user = test();
System.out.println(user);
public static <T> T test() {
	Object user = new User();
	return (T) user;
}

泛型方法的真实案例,可以点击这里查看,基于CountDownLatch的单元测试并发工具类,此案例在实际工作中可以用到,另外我实现了一套泛型的内存队列,写这篇博客也是想总结一下用法,给迷茫的人一些启示。

Class、Class、Class<?>、Class< T>的应用

	* 泛型类
    *     public interface Request<T> {
                void serialize(T t);
          }
    *     声明对象方式:Request request、Request<Object> request,serialize方法的参数T可以是任意类型
    *     声明对象方式:Request<?> request,调用serialize方法,任意类型的参数都不会编译成功
    *     声明对象方式:Request<String> request,serialize方法的参数T只能是String


	* 泛型方法
    *      <V> void doWork(V v){
    *      }
    *      <V> V doWork(){
    *          return (V) null;
    *      }
    *      方法参数V相当于Object,可以是任意类型

在这里插入图片描述

List<T>、List<?>、List<Object>的选择

List<T>、List<?>、List<Object>这三者都可以容纳所有的对象,但使用的顺序应该是首选List<T>,次之List<?>,最后选择List<Object>,原因如下:

  1. List<T>是确定的某一个类型,具体类型在运行期决定;List<?>表示的是任意类型;List<Object>表示List集合中的所有元素都是Object类型;
  2. List<T>可以进行读写操作,例如addremove等操作,因为它的类型是固定的,编码期不需要进行任何的转型操作。List<?>是只读类型,不能进行增加/修改操作,因为编译器不知道真实类型,无法校验类型是否安全,而且它读出的元素都是Object类型的,需要主动转型,所以它经常用于泛型方法的返回值。List<?>虽然不能增加、修改元素,但是可以删除元素,例如removeclear方法,因为删除和泛型类型无关。List<Object>也可以读写操作,但是它执行写入时需要向上转型,在读取数据后,需要向下转型,而此时已经失去了泛型存在的意义。

? extends E? super E什么时候用

  • 泛型结构只参与“读”操作则限定上界(extends关键字),例如下面idea生成的for循环代码,直接使用Integer表示

    public static void read1(List<? extends Integer> list) {
        for (Integer integer : list) {
        }
    }
    
  • 泛型结构只参与“写”操作则限定下界(super关键字),例如下面idea生成的for循环代码,直接使用Object表示

    public static void read(List<? super Integer> list) {
        for (Object o : list) {
        }
    }
    

泛型方法的应用

public final class MapControl {

    private MapControl() {
    }

    /**
     * 单例
     */
    private static final MapControl mapControl;

    /**
     * 数据,实际应用中可以把这个当成RedisTemplate
     * Map<K,V> map; 这种声明是错误的,成员变量中不能出现类上没有定义的泛型
     */
    private static final Map map;

    /**
     * 控制并发20
     */
    private static final Semaphore semaphore;

    static {
        mapControl = new MapControl();
        map = new ConcurrentHashMap<>();
        semaphore = new Semaphore(20);
    }

    private static <K, V> Map<K, V> getMap() {
        try {
            semaphore.acquire();
            return map;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public static void release() {
        semaphore.release();
    }

    private static Object execute(Statement statement) {
        try {
            Map map = MapControl.getMap();
            return statement.prepare(map);
        } finally {
            release();
        }
    }

    interface Statement<K, V> {
        /**
         * 方法返回Object更具有统一性,因为不确定返回数据的的类型,例如方法返回V,返回List<V>,返回Map<K,V>
         * 所以返回Object可以兼容一切返回类型,再强转为需要的类型
         *
         * @param map
         * @return
         */
        Object prepare(final Map<K, V> map);
    }

    public static <K, V> void set(final K key, final V value) {
        MapControl.execute(new MapControl.Statement<K, V>() {
            @Override
            public V prepare(Map<K, V> map) {
                map.put(key, value);
                return null;
            }
        });
    }

    public static <K, V> V get(final K key) {
        /**
         * 返回时强转为V
         */
        return (V) MapControl.execute(new Statement<K, V>() {
            @Override
            public V prepare(Map<K, V> map) {
                return map.get(key);
            }
        });
    }

    public static <K, V> Map<K, V> getAll(final List<K> keys) {
        /**
         * 返回时强转为Map<K,V>类型
         */
        Map<K, V> result = (Map<K, V>) MapControl.execute(new Statement<K, V>() {
            @Override
            public Object prepare(Map<K, V> map) {
                Map<K, V> result = new HashMap<>();
                for (K k : keys) {
                    result.put(k, map.get(k));
                }
                return result;
            }
        });
        return result;
    }

    public static void main(String[] args) {
        MapControl.set(1, "1");
        MapControl.set("name", "p7+");
        Object obj = new Object();
        MapControl.set(obj, obj);

        MapControl.set(2, 2);
        MapControl.set(3, 3);
        MapControl.set(4, 4);

        MapControl.set("name1", "name1");
        MapControl.set("name2", "name2");

        String v = MapControl.get(1);
        System.out.println(v);
        System.out.println(MapControl.get("1"));
        System.out.println(MapControl.get("name"));
        System.out.println(MapControl.get(obj));

        List<Integer> keys = new ArrayList<>();
        keys.add(2);
        keys.add(3);
        keys.add(4);
        System.out.println(MapControl.getAll(keys));

        List<String> strKeys = new ArrayList<>();
        strKeys.add("name");
        strKeys.add("name1");
        strKeys.add("name2");
        System.out.println(MapControl.getAll(strKeys));

    }
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐