多态 & java泛型
多态 & java泛型文章目录多态 & java泛型1、回顾java的多态性1)什么是多态(“多”对1 + 不同“态” => 统一与差异)2)接口和抽象类的比较(同: 抽象方法、继承,异: 多态实现)2、泛型的本质 & 作用1)泛型的本质 - 类型参数化2)泛型的作用3、泛型的使用1)jdk中常见的泛型2)自定义泛型方法(利用反射调用共有方法)1、回顾java的多态性
多态 & java泛型
文章目录
1、回顾java的多态性
参考
1)什么是多态(“多”对1 + 不同“态” => 统一与差异)
学习Java语言的过程中,对于多态的理解是非常关键的,理解了多态也就意味着打开了理解Java各种“抽象”的大门。
所谓的**“多态”,是指不同类型的对象可以响应相同的消息,从相同的基类派生出来的多个类型可被当作同一种类型对待**,可对这些不同的类型进行同样的处理,由于多态性,这些不同派生类对象响应同一方法时的行为是有所差别的 。
java多态性具体体现在定义和功能两个方面,简单的总结一下,多态可以用“三个定义和两个方法”来总结。
- 三个定义分别是父类定义子类构建、接口定义实现类构建和抽象类定义实体类构建
- 两个方法分别是方法重载和方法重写(Override)。
我的理解:三种实现形式 + 两种本质方法
java能够实现多态的形式主要有3种:类继承 & 接口实现 & 泛型
其中java接口是对java类更高程度的抽象,支持多继承和多实现;
而实现多态的方法有重载和重写(Override)
类的继承允许子类重写父类方法;接口实现允许实现类重写父类方法;泛型通过对类型参数化,支持函数的重载。
- 1)多种类型继承同一基类 - 同一基类调用不同类型
public class BaseClass { private String health; private String diploma; public void makeMoney(){ System.out.println("makeMoney"); } } public class AClass extends BaseClass{ private String region_skill_computer; @Override public void makeMoney() { System.out.println("make lot of money"); } } public class BClass extends BaseClass{ private String region_skill_cook; @Override public void makeMoney() { System.out.println("make less money"); } } public static void main(String[] args) { BaseClass a = new AClass(); BaseClass b = new BClass(); a.makeMoney(); b.makeMoney(); }
- 2)多种类型实现相同接口 - 同一接口调用不同实现类
public interface BaseInterface { abstract void makeMoney(); } public class AImpl implements BaseInterface { @Override public void makeMoney() { System.out.println("make lot of money"); } } public class BImpl implements BaseInterface { @Override public void makeMoney() { System.out.println("make less money"); } } public class Main { public static void main(String[] args) { BaseInterface bi = new AImpl(); BaseInterface bi1 = new BImpl(); bi.makeMoney(); bi1.makeMoney(); } }
3)泛型实现函数重载 跳转至自定义泛型方法
这样使得多种类型既能满足形式上的统一,又能拥有自身类型特有的属性或方法;
2)接口和抽象类的比较(同: 抽象方法、继承,异: 多态实现)
抽象类 | 接口 | |
---|---|---|
定义 | 以abstract声明,抽象方法可有可无(!!),相比于普通父类,不能用于实例化。 | 以interface声明,由静态常量和抽象方法组成,abstract,public关键字一般省略。 |
组成 | 构造方法、抽象方法、普通方法、常量、变量。 | 静态常量、抽象方法。 |
使用 | 子类继承抽象类(extends)。 | 子类实现接口(implements)。 |
关系 | 抽象类可以实现多个接口(比如ArrayList,既实现Serializable,又实现Cloneable)。 | 接口不能继承抽象类,但允许继承多个接口。 |
对象 | 都通过对象的多态性产生实例化对象。 | 都通过对象的多态性产生实例化对象。 |
局限 | 抽象类有单继承的局限。 | 接口可以实现多重继承。 |
选择 | 如果抽象类和接口都可以使用的话,优先使用接口,可以避免单继承的局限。 | / |
2、泛型的本质 & 作用
参考
1)泛型的本质 - 类型参数化
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化“带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
2)泛型的作用
我的理解:泛型的使用,意味着其他类型可以复用这个泛型方法/泛型接口/泛型类,泛型通过类型的参数化实现函数的重载,它与接口,抽象类实现多态的方式不同,后者是重写。
简单举几个场景:
- 不想写多个重载函数的场景,如C++的template。
- 约束对象类型的场景,可以定义边界(T extends …),如JDK集合List,Set。
- 用户希望返回他自定义类型的返回值场景,如Json返回Java bean。
- 在用反射的应用中,也经常会用到泛型,如Class。
- 对网页,对资源的分析,返回场景,一般都有泛型。
3、泛型的使用
1)jdk中常见的泛型
参考
单独的T 代表一个类型 ,而 Class<T>
代表这个类型所对应的类, Class<?>
表示类型不确定的类
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
举例说明:
Set<T> 表示 集合里 是 T类的实例
List<E> 表示 集合里 是 E类的实例
List<?> 表示 集合里的对象类型不确定,未指定
List 同 List<?> 是一样的。
在JDK中,常见的泛型接口有:Collection<T>
,Future<T>
;
public interface Collection<E> extends Iterable<E> {...}
public interface Future<V> {...}
泛型类有:Class<T>
,WeakReference<T>
(弱引用类,和GC有关);而在泛型类和泛型接口中就有泛型方法。
/*********Class<T>**********/
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement{
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException{
...
}
}
/********ArrayList<E>***********/
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
}
2)自定义泛型方法(利用反射调用共有方法)
假设这里定义了三种不同形式的时间对象:GMT,UTC,CST
@AllArgsConstructor
@Data
public class TimeZoneObj1 {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT")
private Date startT;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT")
private Date endT;
}
@AllArgsConstructor
@Data
public class TimeZoneObj2 {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Date startT;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Date endT;
}
@AllArgsConstructor
@Data
public class TimeZoneObj3 {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startT;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endT;
}
这里假设我创建了GMT对象列表,并保存为json字符串,接着重新将其封装成GMT,UTC,CST对象列表
/**随机生成GMT日期对象集合,并保存为json文件*/
public void TimeZoneObjGenerator(int num,String outpath){
List<TimeZoneObj1> timeZoneObjs = new ArrayList<>();
for (int i = 0; i < num; i++) {
try {
Date startT = new Date();
Thread.sleep(4000); //时间窗口为4s
Date endT = new Date();
TimeZoneObj1 tz = new TimeZoneObj1(startT,endT);
timeZoneObjs.add(tz);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String json = JSON.toJSONString(timeZoneObjs);
IOUtils.writeJsonFile(outpath,json);
}
这里使用泛型类和泛型方法,对列表中每个GMT,UTC,CST对象进行简单的时间计算和赋值,这里利用反射(Class<T>
泛型类)来获取泛型类型,以及泛型中的方法。
public class Code001_zoneObjCal<T> {
/**
* 对GMT,UTC,CST对象进行计算: startT变为startT + 1s, endT变为EndT - 1s
* 泛型的作用:支持三种不同日期对象的时间操作,提高代码的复用
*/
public List<T> timeCal(List<T> zoneObjs,Class<T> clzz){
/*这里的Class<T> clzz可以直接获取getMethod,而不用将list中取出对象后再getMethod*/
zoneObjs.stream().forEach(zoneObj->{
try {
Method getStartT = zoneObj.getClass().getMethod("getStartT");
Method getEndT = zoneObj.getClass().getMethod("getEndT");
Method setStartT = zoneObj.getClass().getMethod("setStartT",Date.class);
Method setEndT = zoneObj.getClass().getMethod("setEndT",Date.class);
Date startT = (Date)getStartT.invoke(zoneObj);
Date endT = (Date)getEndT.invoke(zoneObj);
startT = DateUtils.getEndT(startT,1); //增加1s
endT = DateUtils.getStartT(endT,1); //减少1s
setStartT.invoke(zoneObj,startT);
setEndT.invoke(zoneObj,endT);
} catch (Exception e) {
e.printStackTrace();
}
});
return zoneObjs;
}
}
更多推荐
所有评论(0)