Java的内部类与向上转型

相关文章:
内部类及其创建方式

在前一篇的文章中,我们讨论了内部类的几种创建的方法,但是,我们会注意到,这些内部类的无论是类的定义还是用来得到其对象的get方法都是public的,因此在其它类中可以直接得到这样的内部类的对象。而在实际的应用中,不同于外部类的只能设计成publicdefault,很多时候内部类的定义是为privateprotected的,此时,我们怎样去使用这些内部类呢?

若我们直接像之前的方法一样去使用这样的内部类,如以下代码:

public class PizzaStore {
    private class SausagePizza{
        int size;
        private SausagePizza(int size) {this.size = size;}
        public void getName() {System.out.println("Got a SausagePizza of size:\t" + size);}
        public int getSize() {return size;}
    }

    public SausagePizza getSausagePizza(int size) {
        return new SausagePizza(size);
    }
}

在这里,我们见到了熟悉的披萨商店,而其中的SausagePizza在这里被定义为private类型,因此,若我们在另一个测试类Test中来直接创建这个类的对象,像如下这样:

public class Test {
    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();
        PizzaStore.SausagePizza pizza = store.getSausagePizza(5);
    }
}

便会出现错误:

Error:(6, 19) java: learning_java.InnerClassTest.PizzaStore.SausagePizza 在 learning_java.InnerClassTest.PizzaStore 中是 private 访问控制

因此,我们可以看到,对于私有的内部类,我们是不能直接在其余的类中创建其对象的,因为我们在类Test中甚至无法获取到PizzaStore.SausagePizza,便无法实现声明,更不用说去创建对象了。

那么,是不是我们无法在除了PizzaStore类之外的类中得到一个披萨的实例呢?

答案是既然允许这样定义内部类,我们自然可以获取这样的实例。但是,在这里我们需要借助向上转型为接口的方式来实现。为什么要借助向上转型为接口呢?因为仔细观察上面的报错,其实我们之所以无法得到一个SausagePizza的实例,在阻碍我们的仅仅是在我们想要获取其实例的时候,无法对其进行声明PizzaStore.SausagePizza而已,而如果这个类是某个接口的实现的话呢?那么我们直接用接口声明不就可以了么?

下面我们继续用我们的披萨商店来作为例子,来看一下我们怎样得到这样的内部类的实例。

首先,我们先定义一个披萨的接口Pizza

public interface Pizza {
    public void getName();
}

然后,我们定义一个起司披萨的内部类来实现这个接口

public class PizzaStore {
    private class CheesePizza implements Pizza {
        private int size;
        private CheesePizza(int size) {this.size = size;}
        public void getName() {System.out.println("Got a CheesePizza of size:\t" + size);}
        public int getSize() {return size;}
    }

    private class SausagePizza{
        private int size;
        private SausagePizza(int size) {this.size = size;}
        public void getName() {System.out.println("Got a SausagePizza of size:\t" + size);}
        public int getSize() {return size;}
    }

    public Pizza getCheesePizza(int size) {
        return new CheesePizza(size);
    }

    public SausagePizza getSausagePizza(int size) {
        return new SausagePizza(size);
    }
}

我们可以看到,在这里新定义的起司披萨CheesePizza与之前的香肠披萨SausagePizza在定义上没有任何不同,只是实现了Pizza接口而已,而用于创建这个内部类的实例的方法getCheesePizza的类型也用其实现的接口Pizza来进行声明,这样,我们在测试程序中便可以用接口的类型Pizza来声明我们所想要创建的实例,如下:

public class Test {
    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();
        Pizza pizza = store.getCheesePizza(5);
        pizza.getName();
    }
}

程序运行结果:

Got a CheesePizza of size:	5

大功告成!

但是,有一点我们需要注意,我们在接口中仅定义了一个方法getName(),而在Test中声明的实例变量pizza的类型为接口Pizza,因此对于这个实例,我们只能调用getName()方法,而不能调用我们在内部类CheesePizza时定义的另一个方法getSize(),否则会报错,例子如下:

public class Test {
    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();
        Pizza pizza = store.getCheesePizza(5);
        pizza.getName();
        // getSize()方法并没有在接口Pizza中声明,因此在这里并不能调用该方法
        pizza.getSize();
    }
}

编译报错:

Error:(8, 14) java: 找不到符号
  符号:   方法 getSize()
  位置: 类型为learning_java.InnerClassTest.Pizza的变量 pizza

由这一点,我们可以看到,private类型的内部类,在其它类中是完全不可见的,且其所实现接口之外的所有方法是不可用的。在这里,我们所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。

由于不能访问任何新增加的、原本不属于公共接口的方法,所以扩展接口是没有太多必要的。

posted @ 2018-11-21 18:23  点点爱梦  阅读(260)  评论(0编辑  收藏  举报