可以将一个类的定义放在另一个类的定义内部,这就是内部类
创建内部类
内部类看起来就像是一种代码隐藏机制,将类放在其他类的内部,内部类和组合是不同的概念
在外部类的方法里面使用内部类的时候与使用普通类没有什么区别
public class Parcel1 {
class Contents { //把类的定义放置在外围类的里面
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTo){
this.label = whereTo;
}
String readLabel(){
return label;
}
}
public void ship (String dest){ //ship方法里面使用内部类,和使用普通类没什么区别
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel1 p1 = new Parcel1();
p1.ship("Hello world");
}
}
public class Parcel2 {
class Contents{
private int i = 10;
private int value(){
return i;
}
}
class Destination{
private String label;
Destination (String whereTo){
this.label = whereTo;
}
String readLabel(){
return label;
}
}
public Destination to( String s){
return new Destination(s);
}
public Contents contents(){
return new Contents();
}
public void ship(String dest){
Contents c = new Contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p2 = new Parcel2();
p2.ship("Hello World");
Parcel2 p3 = new Parcel2();
//如何操作外部类去链接内部类获取内部类对象
Parcel2.Contents c = p3.contents();
Parcel2.Destination d = p3.to("haha"); 钩子
System.out.println(d.readLabel());
}
}
链接到外部类
10.2重点练习
interface Selector{
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] items;
private int next = 0;
public Sequence (int size){
items = new Object[size];
}
public void add (Object x){
if (next < items.length){
items[next ++] = x;
}
}
private class SequenceSelector implements Selector{
private int i = 0;
@Override
public boolean end() {
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if (i < items.length){
i++;
}
}
}
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(7);
for (int i = 0 ; i < 7;i++){
sequence.add(Integer.toString(i));
}
Selector selector = sequence.selector();
while (!selector.end()){
System.out.println(selector.current() + "");
selector.next();
}
}
}
.this 和 .new
如果生成对外部对象的引用的时候,可以使用外部类的名字后面加上.this,这样产生的应用会自动的具有正确的类型
public class DotThis {
void f(){
System.out.println("DotThis.f()");
}
public class Inner{
public DotThis outer(){
return DotThis.this; //生成了对外部对象的引用
}
}
public Inner inner(){
return new Inner();//生成内部类的实例
}
public static void main(String[] args) {
DotThis dt = new DotThis();//创建外部类的实例
DotThis.Inner dti = dt.inner();//调用方法创建内部类的实例
dti.outer().f();//使用内部类的方法来调用了外部类的方法
}
}
.new 使用外部类实例加上.new的形式对内部类进行实例化
public class Parcel3 {
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
Destination(String whereTo){
this.label = whereTo;
System.out.println("wuhu");
}
String readLabel(){
return label;
}
}
public static void main(String[] args) {
Parcel3 p3 = new Parcel3();//创建外围类的实例
Parcel3.Contents c = p3.new Contents();//利用外围类加上.new的方式创建实例
Parcel3.Destination d = p3 .new Destination("Hello");
}
}
内部类和向上转型
当内部类向上转型为基类或者接口的时候,这是因为这个内部类(某个接口的实现)能够完全不可见,而且不能够使用,得到的只是指向基类或者接口的引用,可以隐藏细节
当取得了一个指向基类或接口的引用时,甚至无法找到它的确切类型
class Parcel4{
private class PContents implements Contents{ //私有的。除了Parcel4没有人可以访问
private int i= 0;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destination{ //除了Parcel4和他的子类。
没人可以访问
private String Label;
private PDestination(String whereTo){
this.Label = whereTo;
System.out.println(Label);
}
@Override
public String readLabel() {
return Label;
}
}
public Contents contents(){
return new PContents();//通过方法来返回一个接口的实例
}
public Destination destination(String s){
return new PDestination(s);
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 parcel4 = new Parcel4();
Contents c = parcel4.contents();
Destination d = parcel4.destination("hah");
}
}
在方法和作用域中的内部类
在方法中或者在任意的作用域中定义内部类
(1)实现某个类型的接口,可以创建并且返回对它的引用
局部内部类:在方法的作用域中创建一个类
public class Parcel5 {
public Contents contents() {
class PContents implements Contents {//在方法内部的类,局部内部类
private int i = 11;
@Override
public int value() {
return i;
}
}
return new PContents();//返回接口的实现类
}
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
PDestination(String s1) {
label = s1;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
final Parcel5 parcel5 = new Parcel5();
Contents contents = parcel5.contents();
Destination destination = parcel5.destination("hehe");
System.out.println(contents.value());
System.out.println(destination.readLabel());
}
}
匿名内部类
匿名内部类实际上值得时父类引用指向子类的实现
创建一个继承来自一个接口的匿名类对象,通过new表达式返回的引用被自动转型为对接口的引用
public class Parcel7 {
public Contents contents (){
return new Contents() { //匿名类,单独拿出就是一个类
private int i = 0;
@Override
public int value() {
return i;
}
}; //匿名内部类最后加分号
}
public static void main(String[] args) {
Parcel7 parcel7 = new Parcel7();
Contents c = parcel7.contents();
}
}
在匿名内部类中定义字段的时候还可以对其进行初始化
public class Parcel9 {
public Destination destination(final String desk){
return new Destination() {
private String Label = desk; //对Label进行初始化
@Override
public String readLabel() {
return Label;
}
};
}
public static void main(String[] args) {
Parcel9 parcel9 = new Parcel9();
Destination destination = parcel9.destination("wuhu");
}
}
匿名内部类没有名字,如何匿名内部类中做一些类似构造器的行为?利用实例初始化
abstract class Base{
public Base(int i){
System.out.println("Base Constructor.i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public Base getBase(int i){
return new Base(i) {
{
System.out.println("实例初始化");//使用实例初始化的方式做出类似构造器的行为
}
@Override
public void f() {
System.out.println("匿名类中的f()");
}
};
}
public static void main(String[] args) {
AnonymousConstructor anonymousConstructor = new AnonymousConstructor();
Base base = anonymousConstructor.getBase(10);
base.f();
}
}
public class Parcel10 {
public Destination destination(final String desk, final float price){
return new Destination() {
private int cost;
{
cost = Math.round(price);
if (cost > 500){
System.out.println("超出预算啦");
}
}
private final String Label = desk; //在定义字段时对其进行初始化
@Override
public String readLabel() {
return Label;
}
};
}
public static void main(String[] args) {
Parcel10 parcel10 = new Parcel10();
Destination destination = parcel10.destination("玩具",501.001f);
}
}
匿名内部类和继承相比有些受限,匿名内部类可以扩展类,也可以实现接口,但是不能二者兼备,而且如果实现接口也只能实现一个接口
匿名内部类的工厂方法
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Imp1 implements Service{
@Override
public void method1() {
System.out.println("Imp1.method1()");
}
@Override
public void method2() {
System.out.println("Imp1.method2()");
}
public static ServiceFactory factory(){
return new ServiceFactory() {
@Override
public Service getService() {
return new Imp1();
}
};
}
}
public class Factories {
public static void serviceCun(ServiceFactory serviceFactory){
Service s = serviceFactory.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceCun(Imp1.factory());
}
}
嵌套类
如果不需要内部类对象和外部类对象之间有联系,那么可以将内部类声明称为static,
普通内部类对象隐式的保存一个引用,这个引用指向了创建它的外部类对象,但是如果内部类是static的话就不是这样的了
如果内部类是static的话:如果要创建嵌套类的对象,就不需要外部类的对象了
不能从嵌套类的对象中访问不是静态的外部类对象
普通内部类中的字段和方法只能放在类的外部层次中,普通内部类中不能有static字段和数据也不能有嵌套类,但是嵌套类中可以有这些东西
public class Parcel11 {
private static class ParcelContents implements Contents{
private int i = 0;
@Override
public int value() {
return i;
}
}
protected static class ParcelDestination implements Destination{
private String Label;
ParcelDestination(String whereTo){
this.Label = whereTo;
}
@Override
public String readLabel() {
return Label;
}
}
public static void f(){}
static int x = 10;
static class AnotherLevel{
public static void f(){}
static int x = 20;
}
public static Destination destination(String s){
return new ParcelDestination(s);
}
public static Contents contents(){
return new ParcelContents();
}
public static void main(String[] args) {
Contents c = contents(); //如果内部类是static的话:如果要创建嵌套类的对象
就不需要外部类的对象了
Destination d = destination("hah");
AnotherLevel.f();
f();
System.out.println(x);
System.out.println(AnotherLevel.x);
}
}
接口内部的类
嵌套类可以作为接口的一部分,并且放在接口中的任何类都自动的为public和static的
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
@Override
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
从多层嵌套类中访问外围类成员
一个内部类被嵌套多少层不重要,它都能透明的访问它所嵌入的外围类的所有成员
class MNA{
private void f(){}
class A{
private void g(){}
public class B{
void h(){
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
为什么需要内部类
首先每个内部类都能独立的继承来自一个接口的实现,所以无论外围类是否已经继承过了某个接口类的实现,对内部类来说都是没有影响的
内部类有效的继承了“多重继承”,内部类允许继承多个非接口类型
interface A{}
interface B{}
class X implements A,B{}
class Y implements A{
B makeB(){
return new B() {}; //返回一个B的引用
}
}
public class MultiInterfaces {
static void takesA(A a){}
static void takesB(B b){}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
}
如果拥有的时抽象类或者具体的类不是接口,那么只能使用内部类来实现多继承
class D{}
abstract class E{}
class Z extends D{
E makeE(){
return new E(){};
}
}
public class MultiImplementation {
static void takeD(D d){}
static void takeE(E e){}
public static void main(String[] args) {
Z z = new Z();
takeD(z);
takeE(z.makeE());
}
}
内部类还有一些其他的特性
(1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类对象的信息相互独立
(2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承一个类
(3)创建内部类对象的时刻并不依赖外围类
内部类的继承
内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,哪个指向外围类对象的秘密引用必须被初始化,而在导出类中不再存在可连接的默认对象
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner { //只继承了内部类
InheritInner (WithInner withInner){ //不能只传递一个指向外围类的对象
//必须要在构造器中用super来初始化继承内部类的外围类
withInner.super();
}
public static void main(String[] args) {
final WithInner withInner = new WithInner();
InheritInner inheritInner = new InheritInner(withInner);
}
}
内部类可以被覆盖吗
class Egg{
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk");
}
}
public Egg(){
System.out.println("new Egg");
y = new Yolk();
}
}
public class BigEgg extends Egg{
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk");
}
}
public static void main(String[] args) {
new BigEgg();
}
}
当继承了某个外围类的时候,内部类并没有什么变换,这两个内部类时两个完全独立的实体
class Egg{
private Yolk y;
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk");
}
}
public Egg(){
System.out.println("new Egg");
y = new Yolk();
}
}
public class BigEgg extends Egg{
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk");
}
}
public static void main(String[] args) {
final BigEgg bigEgg = new BigEgg();
BigEgg.Yolk by = bigEgg.new Yolk();
}
}
class Egg2{
protected class Yolk{
public Yolk(){System.out.println("Egg2.Yolk()");}
public void f(){System.out.println("Egg2.Yolk.f()");}
}
private Yolk yolk = new Yolk();
public Egg2(){System.out.println("new Egg2()");}
public void insertYolk(Yolk yolk1){yolk = yolk1;}
public void g(){yolk.f();}
}
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk(){System.out.println("BigEgg2.Yolk()");}
public void f(){System.out.println("BigEgg2.Yolk.f()");}
}
public BigEgg2(){insertYolk(new Yolk());}
public static void main(String[] args) {
final Egg2 egg2 = new BigEgg2();
egg2.g();
}
}
局部内部类
局部内部类一个典型的方式是在方法的方法体内创建,局部内部类中不能有访问说明符,因为它并不是外围类的一部分,但是它可以访问当前代码块的常量,还有他的外围类的所有成员
为什么使用局部内部类而不是匿名内部类?
我们需要一个构造器,或者重载构造器,但是匿名内部类中没有构造器,仅仅只有实例初始化起到了类似构造器的功能
interface Counter{int next();}
public class LocalInnerClass {
private int count =0;
Counter getCounter(String name){ // 局部内部类在方法内部创建
class LocalCount implements Counter{
public LocalCount(){System.out.println("LocalCount()");}
@Override
public int next() {
System.out.print(name);
return count++;
}
}
return new LocalCount();
}
Counter getCounter2(String name){ //匿名内部类
return new Counter() {
{
System.out.println("Counter()");
}
@Override
public int next() {
System.out.print("Counter()");
return count++;
}
};
}
public static void main(String[] args) {
final LocalInnerClass localInnerClass = new LocalInnerClass();
Counter c1 = localInnerClass.getCounter("LocalInner");
Counter c2 = localInnerClass.getCounter2("AnonymousInner");
for (int i = 0 ; i < 5 ; i++){
System.out.println(c1.next()); //两个内部类中的next方法访问的是同一个count
}
for (int i = 0 ; i < 5 ; i ++){
System.out.println(c2.next());
}
}
}