泛型类型的创建
最简单的泛型,相信这种泛型我们最为常见了
List list=new ArrayList<String>();
但很多人仅仅是止步于此,这离掌握泛型其实差的很远。
掌握泛型需要什么呢?
- 使用泛型的时候,知道如何使IDE和编辑器不报错
- 自定义泛型的类型和方法
- 彻底掌握泛型的应用场景和能力边界
很多时候,一些场景有经验的开发者可以隐约感觉到使用泛型可以使代码更加优雅,但是怎么使用,何时使用确实一个难题。
泛型类型
泛型类型分为两种,一种是接口(interface),一种是类(class)。
来写一个简单的泛型类
class Wrapper<T>{
private T INSTANCE;
public T get() {
List list=new ArrayList<String>();
return INSTANCE;
}
public void set(T NEW_INSTANCE) {
this.INSTANCE = NEW_INSTANCE;
}
}
声明类的时候,<T>中的T表示传入的类型。
这个泛型类的使用
Wrapper<String> wrapper = new Wrapper<>();
wrapper.set("Hello World");
String words = wrapper.get();
但此处DEMO仅仅是为了演示泛型类,所以显得多此一举的感觉。
泛型早期最早的应用场景其实是集合类。
让我们对上面的DEMO改造一下。
public class MyList<T> {
private T[] INSTANCE;
public T get(int index) {
return INSTANCE[index];
}
public void set(T newInstance, int index) {
INSTANCE[index] = newInstance;
}
public void add(T newInstance) {
INSTANCE = Arrays.copyOf(INSTANCE, INSTANCE.length + 1);
INSTANCE[INSTANCE.length - 1] = newInstance;
}
}
此时的T[] INSTANCE需要被初始化一下,按照思维惯性,我们的写法会是这样子。
private T[] INSTANCE = T[0]
但是这样写是不行的,具体原因下文会提及,正确的写法应该是
private Object[] INSTANCE=new Object[0];
对应的get()也要做一下类型转换解决报错
public T get(int index) {
return (T)INSTANCE[index];
}
然后尝试使用一下我们创建的这个MyList
MyList<String> list = new MyList<>();
list.add("Hello World");
list.add("I Love JAVA");
list.set("I Love Android", 1);
String indexObject = list.get(0);
这样子的好处是,确保类型统一,list只能传入了我们规定的泛型的类型。
注:泛型并不是为了做到Java做不到的事情,确保类型统一,Java其实很多方法可以做到,但代码量对比泛型都多很多,且不如此可读。
不可静态
在静态的方法和静态对象中,与示例相关时候,是不能使用T的,如DEMO中的private T INSTANCE;改为static T INSTANCE;就会报错。因为泛型在此处没有存在的意义,如果此泛型对象在多个类的被使用,它本身是不会是多个类型。如A类中他是个int,B类中是个char,使用在示例和静态中并没有使用泛型的必要。
泛型类型的继承
首先创建一个商店类型的泛型接口
interface Shop<T> {
T buy();
float refund(T item);
}
我们再来直接写个RealShop来实现此接口看看
public class RealShop implements Shop{
@Override
public Object buy() {
return null;
}
@Override
public float refund(Object item) {
return 0;
}
}
发现所有的T都被换成了Object,泛型被抛弃了。
正确的姿势要在Shop后加<实现类型>
public class RealShop implements Shop<Good>{
@Override
public Good buy() {
return null;
}
@Override
public float refund(Good item) {
return 0;
}
}
想继承Shop并基于泛型继续拓展?
泛型当然是支持的
public interface ExtendShop<T> extends Shop<T> {
void repair(T item);
}
我们给shop传入一个T,ExtendShop也要对应的传入一个T, 于此,我们就给shop增加了维修功能.
T并不是泛型的专用符号,它仅仅是一个标记符号,其实我们可以换成任何名字都可以,只要不会引起冲突。
多类型参数
一个类中是可以引入多个泛型参数的,最直白的例子就是HashMap了,我们来自己实现一个。
public class MyHashMap<K,V> {
public void put(K key,V value){
//省略
}
public V get(K key){
//省略
}
}
继承则继续拿ExtendShop举例子吧。
public interface ExtendShop<T, E> extends Shop<T> {
void repair(T item);
void work(E people);
}
只要对应后面Shop<T>前面有对应的T传入,其他参数只要自行添加即可。
泛型的上界
我们有些时候需要给泛型参数设置一个限制,比如ExtendShop,我希望E是一个List,我们需要设置个extends
public interface ExtendShop<T, E extends List> extends Shop<T> {
void repair(T item);
void work(E people);
}
加上了extends就可以给予E上界,我们初始化的时候,可以
ExtendShop<Object,ArrayList> extendShop;
ExtendShop<Object,LinkedList> extendShop;
ExtendShop<Object,List> extendShop;
但是不能
ExtendShop<Object,String> extendShop;
甚至多个边界都行。
public interface ExtendShop<T, E extends String & List & Collection> extends Shop<T> {
void repair(T item);
void work(E people);
}
需要注意的是,边界之内只能有一个类,且只能写在最左边。因为只能有一个父类,泛型也逃离不了这个限制。