不知道泛型,面试官露出了朱一dan一样的笑容,我差点就去了非洲。。。。


之前我一直认为泛型不就是定义了参数类型,定义集合存储的元素类型吗?有什么好理解的?直到入职后看别人写的代码,各种用泛型,可是我只会new ArrayList,只能借着摸鱼的时间好好学习一下泛型的用法。

为什么要学习泛型?

先看看优秀的jdk源码吧

//hashmap
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
//hashmap中比较元素是否相同的equals方法
public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
//Callable接口
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
//公司项目写的接口 
<T extends BusinessObject> T findOne(Serializable objID, Class<T> refer)

说白了,我现阶段主要是为了能看懂别人的代码,别人是怎么设计代码的,这样的好处是什么。

泛型是什么?

列一段Oracle官网的例子,我之前也只会这一种,哈哈哈:

ArrayList arrayList = new ArrayList();
arrayList.add(new Integer(0));
Integer i = (Integer)arrayList.iterator().next();

可以看出来,如果不使用泛型,那么第三行是需要强制类型转换的,因为ArrayList可以放任何Object及其子类类型的数据。

ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(new Integer(0));
Integer i =  arrayList.iterator().next();

使用泛型之后,不需要强制类型转换了,这儿也就是起到一个规定Array List这个容器中可以存放什么类型数据的作用。

潜意识里面,泛型中很容易出错的地方,之前面试就跳进了这个坑中

ArrayList<Integer> arrayList = new ArrayList<>();
Integer i =  arrayList.iterator().next();
ArrayList<Object> ls = arrayList;//报错

ls.add("aa");
arrayList.add(2);
ls.get(0);

之前面试官问我前三行代码有什么问题吗?我毅然决然的回答,对啊。然后面试官露出朱一旦般的笑容,可能你们也想到了,这篇文章是在非洲一个小黑网吧写的。

回到主题,为啥第三行就报错了?Integer难道不是Object的子类吗?难道不是亲生的???
在这里插入图片描述

看一看Oracle官方怎么说的

Here we've aliased ls and lo. Accessing ls, a list of String, through the alias lo, we can insert arbitrary objects into it. As a result ls does not hold just Strings anymore, and when we try and get something out of it, we get a rude surprise.
In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>. This is probably the hardest thing you need to learn about generics, because it goes against our deeply held intuitions.

We should not assume that collections don't change. Our instinct may lead us to think of these things as immutable.

大致意思就是ls现在的实体是array List,那么就应该只能存储Integer类型的,因为arrayList已经定义泛型的Integer了。如果设置了泛型是Integer,但是取出来一个String,那不是蒙蔽了。(我是这么理解的,如果有不对请大哥们不吝指教)

为了避免自己被送到非洲,我赶紧学习了一下泛型的使用。

1.泛型类

就是在类上加泛型,典型的就是各种集合类,List、Set、Map

public class Beijin {
   public void say() {
        System.out.println("我是北京人");
    }
}
public class Shanghai {
    public void say() {
        System.out.println("我是上海人");
    }
}

public class People<T> {
    T  getPeople(T t){
        return t;
    }

    public static void main(String[] args) {
        People<Beijin> beijinPeople = new People<>();
        Beijin beijin = new Beijin();
        beijinPeople.getPeople(beijin).say();

        People<Shanghai> shanghaiPeople = new People<>();
        Shanghai shanghai = new Shanghai();
        shanghaiPeople.getPeople(shanghai).say();
    }
}
echo:
我是北京人
我是上海人

也就是创建对象实体的时候才去指定是具体是哪个类。也就相当于我可以new People,也可以new People,起到代码重用作用吧,我觉得有点类似与适配器原理,把参数换成Object类型也可以,只不过后面会需要强制类型转换。

2.泛型接口

public interface AAAInterface<T> {
    public T say();
}
public class AAAImpl<T> implements AAAInterface<T>{
    @Override
    public T say() {
        return null;
    }
}

这不是和泛型类差不多吗?只不过是把泛型定义在了接口上,然后提供给下面的方法使用。

3.泛型方法

public class Cat {
    public <T> void say(T t){
        System.out.println(t);
    }

    public <K,V> K eat(V v){
        System.out.println("我在吃"+v);
        return (K)"金克拉";
    }
    
    public <M extends Number> M getNumber(M m){
        return m;
    }

    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.say("我们一起学猫叫");

        cat.eat("小鱼干");
    }
}
echo:
我们一起学猫叫
我在吃小鱼干

可以看到类上面并没有泛型,泛型类型都在方法上面和方法里面,这儿权限修饰符和返回值类型之间的不能丢,他就是标识这是一个泛型方法。像getNumber方法中,还可以直接定义后面两个M的取值范围,用的是泛型上下边界。

泛型通配符

/**
 * 通配符的使用
 * 使用通配符之后就只能调用与类型无关的方法,因为类型还没有确定add方法就不能使用
 */
class Obj3{
    public void say(List<?> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

这儿的List就可以存放任何类型的。

泛型方法,泛型类,泛型通配符对比

/**
 * 通配符的使用
 * 使用通配符之后就只能调用与类型无关的方法,因为类型还没有确定add方法就不能使用
 */
class Obj3{
    public void say(List<?> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
/**
 *泛型类
 */
class Obj3<T>{
    public void say(List<T> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
/**
 *泛型方法
 */
class Obj3{
    public <T> void say(List<T> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

泛型类相对于泛型方法和通配符不灵活,泛型类需要在创建类对象的时候就去执行泛型类型,但是泛型方法和通配符不需要。通配符通常能被泛型方法代替,如果参数之间有依赖关系,或者返回值与泛型有依赖关系,就使用泛型方法;如果没有依赖关系就使用泛型通配符,泛型通配符更灵活一些

泛型上下边界

/**
 * 如果我们只想操作数字类型的话怎么办
 * 设定通配符上限
 */
class Obj4{
    public void say(List<? extends Number> list){
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
    }
}

/**
 * 设定通配符下限
 * List存的对象只能是String 及其 父类
 */
class Obj5{
    public void say(List<? super String> list){
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
    }
}
        list.get(i);
    }
}

}

/**

  • 设定通配符下限
  • List存的对象只能是String 及其 父类
    */
    class Obj5{
    public void say(List<? super String> list){
    for (int i = 0; i < list.size(); i++) {
    list.get(i);
    }
    }
    }



好多地方不知道怎么表达,还是理解的不够好,有时间在修改一遍,加油!!!
立个flag,每周写一篇文章提升自己。加油加油加油!!!
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200725154210851.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDY5MzYzOA==,size_16,color_FFFFFF,t_70)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值