内部类

本文详细解析了Java内部类的创建、使用及特性,包括在非静态方法中使用内部类、内部类对象的创建、向上转型、在方法和作用域内的使用、匿名内部类、嵌套类以及内部类的优势。通过多个示例展示了内部类如何访问外部类成员,以及其在实现接口、隐藏实现细节、创建辅助类和实现闭包与回调中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1内部类的创建和使用

(1)在外部类的非静态方法中使用内部类:与使用普通类没啥区别

(2)在外部类的非静态方法之外的任意位置创建某个内部类的对象,格式:OuterClassName.InnerClassName

public class Outer {
    class Inner{
        public void print(){
            System.out.printf("我是内部类");
        }
    }

    public Inner returnInner(){
        return new Inner();
    }

    public static void main(String[] args) {
        Outer o=new Outer();
        Outer.Inner inner=o.returnInner();
        inner.print();
    }
}
 
//output:我是内部类

(3)内部类除了是一种名字隐藏和组织代码的模式。它还能访问其外围对象的所有成员而不需要任何特殊条件。此外,内部类还拥有其外部类的所有元素的访问权。

(4)在内部类中对外部类的引用.this

public class Outer {
    void f() {
        System.out.printf("Outer.f()");
    }

    public class Inner {
        public Outer returnOuter() {
            return Outer.this;
        }

    }

    public Inner inner() {
        return new Inner();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        ;
        Outer.Inner oui = outer.inner();
        oui.returnOuter().f();
    }
}

(5)创建内部类对象必须使用外部类对象,如果创建静态内部类对象,则不用。例:

public class Outer {
    public class Inner {
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner oui = outer.new Inner();
    }
}

2内部类向上转型

内部类实现一个接口,访问修饰符设置为private或protected,能够隐藏实现细节。

public interface Contents {
    int value();
}

class Parcel{
    private class PContents implements Contents{
        private int i=11;
        public int value(){return i;}
    }
    
    public Contents contents(){
        return new PContents();
    }

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

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

使用情况:

(1)实现了某类型的接口,可以创建并返回对其的引用。

(2)要解决一个复杂的问题,想创建一个类来辅助解决方法,但又不希望这个类是公共可用的。

例1:定义在方法中的内部类。只在所定义的方法内可用。

public interface Destination {
    String readLabel();
}

class Parcel{
    public Destination destination(String s){
        class PDestination implements Destination{
            private String label;

            public PDestination(String label) {
                this.label = label;
            }

            @Override public String readLabel() {
                return null;
            }
        }
    return new  PDestination(s);    
        
    }

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

注:PDestination类是destination()方法的一部分,而不是Parcel的一部分,所以在destination()之外不能访问PDestination

例2:在任意作用域内嵌入一个内部类

public class Parcel {
    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) {
        Parcel p = new Parcel();
        p.track();
    }
}

注:该内部类只在if语句作用域内可用。

4匿名内部类

例1:

interface Contents {
    int value();
}

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

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

例2:在匿名类中定义字段时还能够对其执行初始化操作:

interface Destination{
    String readLabel();
}

public class Parcel {
    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) {
        Parcel p=new Parcel();
        Destination d=p.destination("abc");
    }
}

注意:要想使内部类访问外部类的变量, 变量要加final

例3:在匿名类中不可能有命名构造器(因为它根本没有名字),但通过实例初始化,能够达到为匿名内部类创建一个构造器的效果。但是不能重载实例初始化方法,仅有一个这样的构造器。

abstract class Base{
    public Base(int i){
        System.out.printf("Base constructor,i="+i);
    }

    public abstract void f();
}
public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i){
            {
                System.out.printf("Inside instance initializer");
            }

            public void f(){
                System.out.printf("In anonymous f()");
            }
        };
    }

    public static void main(String[] args) {
        Base base=getBase(47);
        base.f();
    }
}

注:此例中不要求变量一定是final的。因为i被传递给匿名类的基类的构造器,它并不会在匿名内部类被直接使用。

匿名内部类能继承类也可实现接口,但不能二者兼备,实现接口也只能实现一个接口。

例4使用匿名内部类的工厂方法

interface Service{
    void method1();
    void method2();
}

interface ServiceFactory{
    Service getService();
}

class Implementation1 implements Service{
    private Implementation1() {
    }

    @Override public void method1() {
        System.out.printf("Implementation1 method1");
    }

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

class Implementation2 implements Service{
    private Implementation2() {
    }

    @Override public void method1() {
        System.out.printf("Implementation2 method1");
    }

    @Override public void method2() {
        System.out.printf("Implementation2 method2");
    }
    
    public static ServiceFactory factory=new ServiceFactory() {
        @Override public Service getService() {
            return new Implementation2();
        }
    };
}
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);
    }
}

5嵌套类(静态匿名内部类)

普通内部类对象隐式地保存了一个引用,指向创建它的外围类对象。

而嵌套类:(1)要创建嵌套类的对象,并不需要访问外围类的对象

(2)不能从嵌套类的对象中访问非静态的外围类对象

(3)普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但嵌套类可以包含所有这些东西。

例1:

interface Contents {
    int value();
}

interface Destination {
    String readLabel();
}

public class Parcel {
    private static class ParcelContents implements Contents{
        private int i=11;
        public int value(){return i;}
        
    }
    protected static class ParcelDestination implements Destination{
        private String label;
        private ParcelDestination(String whereTo){label=whereTo;}
        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("abc");
    }
}

例2:接口的内部类

正常情况下不能在接口中放任何代码,但嵌套类可以作为接口的一部分。

接口中的任何类都自动地是public和static的。因为类是static的,只是将嵌套类置于接口的命名空间中,并不违反接口的规则。甚至可以在内部类中实现外围其接口。

public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface{
        public void howdy(){
            System.out.printf("howdy");
        }

        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}

如果想创建某些公共代码,使得它们可以被某个接口的所有不同实现所共有,那么使用接口内部的嵌套类会显得很方便。

例2从多层嵌套类中访问外部类成员

一个内部类被嵌套多少层并不重要,它能透明地访问所有它嵌入的外围类的所有成员。

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

可以看到在MNA.A.B中,调用方法g()和f()不需要任何条件(即使它们被定义为private)

6为什么需要内部类

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;
        public boolean end(){return i==items.length;}
        public Object current(){return items[i];}
        public void next(){if(i<items.length) i++;}
    }

    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] args) {
        Sequence sequence=new Sequence(10);
        for(int i=0;i<10;i++){
            sequence.add(Integer.toString(i));

        }
        Selector selector=sequence.selector();
        while (!selector.end()){
            System.out.printf(selector.current()+" ");
            selector.next();
        }
    }
}

???如果Sequence不使用内部类,就必须声明Sequence是一个Selector,对于某个特定的Sequence只能有一个Selector。然而使用内部类很容易就能拥有另一个方法reverseSelector(),用它来生成一个反方向遍历序列的Selector。只有内部类才有这种灵活性

7闭包与回调

闭包:一个可调用对象,记录了一些信息,这些信息来自于创建它的作用域。

由此定义可以出内部类是面向对象的闭包。它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类可以操作所有成员包括private成员。

回调:通过回调对象能够携带某些信息,这些信息允许它在稍后的某个时刻调用初始的对象。

interface Incrementable{
    void increment();
}
class Callee1 implements Incrementable{
    private int i=0;

    @Override public void increment() {
        i++;
        System.out.printf(i+"");
    }
}

class MyIncrement{
    public void increment(){
        System.out.printf("Other Operation");

    }
    static void f(MyIncrement mi){mi.increment();}
}

class Callee2 extends MyIncrement{
    private int i=0;
    public void increment(){
        super.increment();
        i++;
        System.out.printf(i+"");
    }
    private class Closure implements Incrementable{
        public void increment(){
            Callee2.this.increment();
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}

class Caller{
    private Incrementable callbackReference;
    Caller(Incrementable cbh){callbackReference=cbh;}
    void go(){callbackReference.increment();}
}
public class Callbacks {
    public static void main(String[] args) {
        Callee1 c1=new Callee1();
        Callee2 c2=new Callee2();
        MyIncrement.f(c2);
        Caller caller1=new Caller(c1);
        Caller caller2=new Caller(c2.getCallbackReference());
        caller1.go();
        caller1.go();
        caller2.go();
        caller2.go();

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值