学习 Java编程思想 Thinking in Java 第十章:内部类

前言

在读 《Java编程思想 Thinking in Java》 这本书的时候,会有很多示例代码。为了巩固和实践,所以将书上的代码都写上一遍

10.5 在方法和作用域内的内部类

先写个接口Destination:

package com.innerClass;

public interface Destination {
    String readLabel();
}

接下来就写,在方法的作用域中创建一个完整的类。这被叫做局部内部类!!!

package com.innerClass;

public class Parcel5 {
    public Destination destination(String s){
        class PDestination implements  Destination{
            private String label;
            private PDestination(String whereTo){
                label = whereTo;
            }
            public String readLabel(){
                return label;
            }
        }
        return  new PDestination(s);
    }

    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
    }
}

下面展示,如何在任意作用域内嵌入一个内部类:

package com.innerClass;

public class Parcel6 {
    private void internalTracking(boolean b){
        if(b){
            class TrackingSlip{
                private String id;
                TrackingSlip(String s){
                    id = s;
                }
                String getSlip(){
                    return id;
                }
            }
            TrackingSlip ts = new TrackingSlip("slip");
            String s = ts.getSlip();
        }
        //试想一下上面这两句话,写到这里可不可以呢?
    }
    public void track(){
        internalTracking(true);
    }
    public static void main(String[] args) {
        Parcel6 p = new Parcel6();
        p.track();;
    }
}

回答代码中的问题,答案是不可以哦,因为那里是 if的作用域外,超出了TrackingSlip的作用域了!

10.6 匿名内部类

先写个接口,做铺垫啊

package com.innerClass;

public interface Contents {
    int value();
}

马上写一个匿名内部类啊

package com.innerClass;

public class Parcel7 {
    public Contents contents(){
        return new Contents() {
            private int i = 11;
            @Override
            public int value() {
                return i;
            }
        };
    }

    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Contents c = p.contents();
    }
}

在contents方法中,return的那个类,没有名字,看吧,是不是很奇怪。
在return一个new的Contents的时候,仿佛听见一个声音,说:“等等,我想在这里插入一个内的定义”。结果就成这个样子了。
这就是匿名内部类啊!!!
对这个奇怪的语法正儿八经的总结一下就是:“创建一个继承自Contents的匿名内部类的对象。通过new表达式返回的引用被自动向上转型为对Contents的引用。

上述匿名内部类的语法是下述形式的简化形式:

package com.innerClass;

public class Parcel7b {
    class MyContents implements Contents{
        private int i = 11;
        @Override
        public int value() {
            return i;
        }
    }
    public Contents contents(){
        return new MyContents();
    }
    public static void main(String[] args) {
        Parcel7b p = new Parcel7b();
        Contents c = p.contents();
    }
}

有没有觉得第一种书写方式更装逼一点呢!

咦?咦咦咦?
我发现上面那个匿名内部类使用了默认构造器来生成Contents,但是如果基类需要一个有参数的构造器,就像下面这样,改怎么办呢?

package com.innerClass;

public class Parcel8 {
    public Wrapping wrapping(int x){
        return new Wrapping(x){
            @Override
            public int value() {
                return super.value() * 47;
            }
        };
    }
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(10);
    }
}

Wrapping这个类我该怎样写,才能正确的被Parcel8使用呢?
很简单!只需要传递合适的参数给基类的构造器即可,就像这样:

package com.innerClass;

public class Wrapping {
    private int i;
    public Wrapping(int x){
        i = x;
    }
    public int value(){
        return i;
    }
}

虽然Wrapping只是一个具有具体实现的普通类,但还是被其导出类当作公共“接口”来使用!

如果在匿名类中定义字段时,还要对其执行初始化操作,该咋写呢?就该像下面这样写呀:

package com.innerClass;

public class Parcel9 {
    public Destination destination(final String dest){
        return new Destination() {
            private String label = dest;
            @Override
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.destination("Tasmania");
    }
}

注意到了吗?用到了关键字final哦,如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象时,那么编译器就会要求其参数是final的。

实现一个为匿名内部类创建一个构造器的效果:

package com.innerClass;

public abstract class Base {
    public Base(int i){
        System.out.println("Base constructor, i = "+i);
    }
    public abstract void f();
}
package com.innerClass;

import com.sun.org.apache.xpath.internal.SourceTree;

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i) {
            @Override
            public void f() {}
        };
    }
    public static void main(String[] args) {
        Base base = getBase(47);
        base.f();
    }
}

这个例子中呢,变量i,不要求是final的。因为 i 被传递给匿名类的基类的构造器,并没有在匿名类内部直接使用。

带实例初始化的“parcel”形式。

package com.innerClass;

public class Parcel10 {
    public Destination destination(final String dest, final float price){
        return new Destination() {
            private int cost;
            {
                cost = Math.round(price);
                if(cost > 100){
                    System.out.println("Over budget");
                }
            }
            private String label = dest;
            @Override
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel10 p = new Parcel10();
        Destination d = p.destination("Tasmania", 101.395F);
    }
}

10.6.1 再访工厂方法
使用匿名内部类改造 interfaces/Factories.java 示例:

package com.innerClass;

public interface Service {
    void method1();
    void method2();
}
package com.innerClass;

public interface ServiceFactory {
    Service getService();
}
package com.innerClass;

public class Implementation1 implements Service{
    private Implementation1(){}
    @Override
    public void method1() {
        System.out.println("Implementation1 method1");
    }

    @Override
    public void method2() {
        System.out.println("Implementation1 method2");
    }
    public static ServiceFactory factory = new ServiceFactory() {
        @Override
        public Service getService() {
            return new Implementation1();
        }
    };
}
package com.innerClass;


public class Implementation2 implements Service{
    private Implementation2(){}
    @Override
    public void method1() {
        System.out.println("Implementation2 method1");
    }

    @Override
    public void method2() {
        System.out.println("Implementation2 method2");
    }
    public static ServiceFactory factory = new ServiceFactory() {
        @Override
        public Service getService() {
            return new Implementation2();
        }
    };
}
package com.innerClass;

public class Factories {
    public static void serviceConsumer(ServiceFactory fact){
        Service s = fact.getService();
        s.method1();
        s.method2();
    }

    public static void main(String[] args) {
        serviceConsumer(Implementation1.factory);
        serviceConsumer(Implementation2.factory);
    }
}

改造 interfaces/Games.java 示例:

package com.innerClass.games;

public interface Game {
    boolean move();
}
package com.innerClass.games;

public interface GameFactory {
    Game getGame();
}
package com.innerClass.games;

public class Checkers implements Game{
    private Checkers(){}
    private int moves = 0;
    private static final int MOVES = 3;

    @Override
    public boolean move() {
        System.out.println("Checkers move " + moves);
        return ++moves != MOVES;
    }
    public static GameFactory factory = new GameFactory() {
        @Override
        public Game getGame() {
            return new Checkers();
        }
    };
}
package com.innerClass.games;

public class Chess implements Game{
    private Chess(){}
    private int moves = 0;
    private static final int MOVES = 4;
    @Override
    public boolean move() {
        System.out.println("Chess move "+ moves);
        return ++moves != MOVES;
    }
    public static GameFactory factory = new GameFactory() {
        @Override
        public Game getGame() {
            return new Chess();
        }
    };
}
package com.innerClass.games;

public class games {
    public static void playGame(GameFactory factory){
        Game s = factory.getGame();
        while (s.move()){
            ;
        }
    }

    public static void main(String[] args) {
        playGame(Checkers.factory);
        playGame(Chess.factory);
    }
}

10.7 嵌套类

如果不需要内部类对象与外围对象之间有联系,那么可以讲内部类声明成static。这就被称为:嵌套类。
要想理解static应用于内部内时的含义,就必须必须必须的记住一点:普通内部类对象其实隐式的保存了一个引用,这个引用只想它外围的对象。
上菜:

package com.innerClass;

public class Parcel11 {
    private static class ParcelContents implements  Contents{
        private int i = 1;
        @Override
        public int value() {
            return i;
        }
        protected static class ParcelDestination implements  Destination{
            private String label;
            private ParcelDestination(String whereTo){
                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 = 10;
            }
        }
        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();
            Destination d = destination("Tasmania");
        }
    }
}

在main()中,没有用到Parcel11对象就可以直接使用static修饰的内部类啦,也就是嵌套类。

10.7.1 接口内部的类
这就厉害了,就像下面这样:

package com.innerClass;

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();
        }
    }
}

————————————————————————————
技术无他,唯有熟尔。
知其然,也知其所以然。
踏实一些,不要着急,你想要的岁月都会给你。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值