一、背景
今天是学习JAVASE的第20天,和大家汇报一下前几天在学习LIst集合(主要是ArrayLIst),我写了一个计算集合内数据的和的工具方法,然后封装好丢给了好朋友,他接受到之后就跟我说程序运行不了我就纳了个闷,怎么会运行不了呢?经过询问才知道,他输入了“一”和“三”........这字符串怎么用来计算嘛。我就抛出了这个问题,有啥方法可以根据我输入的参数的类型来处理集合吗?经过我的研究,这种技术就叫——泛型。
二、为什么要有泛型
众所周知,集合里面的元素都是被上升为Object类型的,存的时候你是心高气傲,取出来用的时候就生死难料了(doge),如下
public static void main(String[] args) {
//往集合里存点东西
ArrayList list = new ArrayList();
list.add("一亩三分地er");
list.add("求免费的赞和评论给予我鼓励呀");
list.add(123);
list.add(new Student());
//使用集合里的元素
String str = new String();
for (int i = 0; i < list.size(); i++) {
str+=list.get(i);
}
}
这铁定报错啊,怎么可能把一个类给拼接到字符串上呢?所以,要保证不报错,我就只能对集合里的元素使用Object类的方法,满足了通用性。可是这样就 牺牲了独特性,因为我的元素的特别方法就用不了了。所以才出现了——泛型这一技术。
通过泛型,我们就可以实现我要用String类,这个集合就是String类型的集合;我用Integer类,他就是Integer类型的集合,实现了通用性和独特性的结合。
三、泛型类、泛型方法、泛型接口
1、泛型类
要想使用泛型类,只需要在类名的后面加“<>”符号,<>里面可以添加一个或多个符号。这个符号可以是A、B、C等等符合命名规范的字符,但一般使用约定俗成的字符,如:T(type)、E(element)、Key(键)等等。这样在类中就可以替掉具体的类型了。这个符号可以看作一个参数。使用时,调用者传入什么类型参数,这个符号就会变成什么类型。
public class ArrayList1<E> {
transient Object[] elementData;
public E get(int index) {
return (E) elementData[index];
}
public boolean add (E e) {
//省略向elementData添加E元素的代码
return true;
}
}
比如,上面的ArrayList1类就需要一个E类型作为参数用于类中的方法和属性。
2、泛型方法
首先说好,泛型方法不需要所在的类是泛型类,泛型方法和泛型类没有必反联系,泛型方法一般用于参数E只在这个方法会被用到,如果在整个类中被用到那就可以把泛型定义在类名后面啦!
public class GenericTest {
public <E> E genericMethod(E e){
//第一个E表示这是一个泛型方法,第二个E表示返回值类型为E,小括号里的E表示形参为E类型
//省略逻辑处理
return e;
}
}
上面的方法是成员方法,当然泛型也可以被用在静态方法。但是静态属性和静态方法有个缺陷,就是他们无法访问类上定义的泛型参数:
public class GenericTest<E> {
private static void generiMethond(E e) {
}//编译错误
}
这很好理解嘛,因为泛型参数是我们在创建实例对象的时候指定的,而静态方法是class加载时就被创建的,没有给他指定泛型参数的类型,他是不可能让你编译成功的。
所以,静态方法要调用泛型参数,那么他本身就要被声明为一个泛型、静态方法。即:
public class GenericTest<E> {
private static <E> void back(E e) {
}//编译错误
}
3、泛型接口
泛型接口的使用有两种方法:
1、实现类给出具体的类型
2、实现类延续泛型,在创建对象时给出具体的泛型类型。
我们来看第一种:
public interface Generic3<E> {
void consum(E e);
}
class Test implements Generic3<String>{
@Override
public void consum(String s) {
}
}
可以看到,当我们Test实现类给出具体的类型后(String),我们以后传递的参数就只能是String了,包括后面实现Testt时,传递的参数也只能是String。
第二种:
public interface Generic3<E> {
void consum(E e);
}
class Test<E> implements Generic3<E>{
@Override
public void consum(E e) {
}
}
public class Generic {
public static void main(String[] args) {
Test<String> t = new Test<>();
t.consum("aaa");
}
}
在实现接口时,我们并没有给出E的具体类型,而是在实现类实例化的时候给出的(为String),此时我们调用consume方法就只能传递String类型的变量。
四、结语
这篇文章可能不如某些行内大佬做的好,大家多多包容,有问题欢迎在评论区批评指正!祝生活愉快!