多态 & 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、泛型的使用

参考熬了个大夜总算把Java中的泛型吃透了

1)jdk中常见的泛型

参考

单独的T 代表一个类型 ,而 Class<T>代表这个类型所对应的类Class<?>表示类型不确定的类

E - Element (在集合中使用,因为集合中存放的是元素)
T - TypeJava 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? -  表示不确定的java类型

举例说明: 
Set<T> 表示 集合里 是   T类的实例 
List<E> 表示  集合里 是  E类的实例 
List<?> 表示 集合里的对象类型不确定,未指定 
ListList<?> 是一样的。 

在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
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值