【Java基础】使用接口和实现类声明变量的区别

88 阅读2分钟

内容提要:

笔者在日常学习中遇到一个问题:不明白使用Map<k, v>和HashMap<k, v>声明变量的区别,毕竟他们都可以持有通过new HashMap<k, v>方式实例化的对象。现在对自己的理解作一个总结。

正文

二者的区别主要在以下几点:

1. 访问权限

若使用一个接口声明变量,并使用具体的实现类实例化对象,那么该变量只能访问接口中已声明的变量和方法;若使用具体的类,那么变量可以访问该类中定义的、但未在接口中定义的变量和方法:

//object1能访问Map接口中定义的变量和方法
Map<key, value> object1 = new HashMap<>();

//object2能访问HashMap类中定义的变量和方法
HashMap<key, value> object2 = new HashMap<>();

这类似于类的继承与多态,如用父类声明变量并引用子类对象,那么该变量只能访问父类中的方法字段:

//Test.java
package com.java;

interface Test {
    void output1();
    void output2();
}

//TestFather.java
package com.java;

//TestFather实现Test接口
public class TestFather implements Test{
    public void output1() {
        System.out.println("This is TestFather.output1");
    }
    public void output2() {
        System.out.println("This is TestFather.output2");
    }

}

//TestSon.java
package com.java;

//TestSon继承TestFather类
public class TestSon extends TestFather {
    public void output1() {
        System.out.println("This is TestSon.output1");
    }
    void output3() {
        System.out.println("This is TestSon.output3");
    }
}

//Main.java
package com.java;

public class Main {
    public static void main(String[] args) {
        TestFather obj1 = new TestSon(); //父类声明变量持有子类对象
        TestSon obj2 = new TestSon(); //具体的子类声明变量持有对象
        obj1.output3(); //obj1由父类声明,不能访问子类中的方法
        obj1.output2(); //调用TestFather中的output2方法
        obj2.output3(); //调用TestSon中的output3方法
        
        //由接口声明的变量可以持有相应的各种实现类的对象
        Test obj3 = new TestSon(); 
        obj3 = obj1;
        obj3 = obj2;
    }
}

2. 具体性和灵活性

使用具体的类声明一个变量,限制了该变量只能引用该具体类的实例。

若使用接口或父类声明变量,那么该变量可以灵活地引用该接口实现类的实例或该父类的子类的实例。例如:

Map<key, value> object2 = new HashMap<>();

可以在需要的时候轻松地切换实现。比如将HashMap切换为TreeMap或其他实现了Map接口的类,这样便于代码的扩展和维护。

总结

使用具体的实现类可以访问给类中特有的方法和属性,使用接口(或某个具体类的父类)声明变量并用实现类(或子类)创建对象可以使代码更加抽象和通用。