相关文章:
内部类及其创建方式
在前一篇的文章中,我们讨论了内部类的几种创建的方法,但是,我们会注意到,这些内部类的无论是类的定义还是用来得到其对象的get方法都是public
的,因此在其它类中可以直接得到这样的内部类的对象。而在实际的应用中,不同于外部类的只能设计成public
与default
,很多时候内部类的定义是为private
或protected
的,此时,我们怎样去使用这些内部类呢?
若我们直接像之前的方法一样去使用这样的内部类,如以下代码:
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
类型的内部类,在其它类中是完全不可见的,且其所实现接口之外的所有方法是不可用的。在这里,我们所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。
由于不能访问任何新增加的、原本不属于公共接口的方法,所以扩展接口是没有太多必要的。