Java捡漏

内部类

2018-08-10  本文已影响9人  shenyoujian

一、为什么要使用内部类

1、内部类可以访问其外围类所有元素。
2、每个内部类都能独立继承一个接口的实现,或继承一个类,或者说外部类通过这种方式来实现“多重继承”。

学习了集合之后,现在我们有一个想法就是来创建一个属于自己的集合数组类MyArrayList,我们都知道集合类为了可以使用增强for,都必须实现Iterable接口。

public class MyArrayList<AnyType> implements Iterable<AnyType> {

    private static final int DEFAULT_CAPACITY = 10;     // 数组的容量
    private int theSize;                                   // 数组中的当前项数
    private AnyType[] theItems;                         // 基础数组


    @Override
    public Iterator<AnyType> iterator() {
        return null;
    }
}

而实现Iterable接口必须实现iterator方法,该方法返回一个Iterator(迭代器模式的例子)接口的实例。我们一般想的就是那我们再写一个Iterator接口的实现类,然后再iterator方法中返回null改为该类实例就好啦。

// Iterator接口
public void Iterator<AnyType>{
  boolean hasNext();
  AnyType next();
}
public class ArrayListIterator implements Iterator {

    private int current = 0;                // 迭代序列中下一项的下标
    MyArrayList list;                       // 数组集合的引用

    @Override
    public boolean hasNext() {
        return current < list.theSize;
    }

    @Override
    public Object next() {
        if (!hasNext())
            throw new NoSuchElementException();
        return list.theItems[current++];
    }
}

可是上面都报错了,原因是ArrayListIterator类中就算有数组集合的引用也访问不了MyArrayList类中的theSize和theItems啊,因为这些属性都是私有的。ok我改

那有什么方法既可以访问其他类的私有属性并且没有上面的烦恼呢?使用内部类

我们把ArrayListIterator作为MyArrayList的一个内部类,如下:

public class MyArrayList<AnyType> implements Iterable<AnyType> {

    private static final int DEFAULT_CAPACITY = 10;     // 数组的容量
    private int theSize;                                // 数组中的当前项数
    private AnyType[] theItems;                         // 基础数组


    //内部类
    public class ArrayListIterator implements Iterator {

        private int current = 0;                        // 迭代序列中下一项的下标
        @Override
        public boolean hasNext() {
            return current < theSize;
        }

        @Override
        public AnyType next() {
            if (!hasNext())
                throw new NoSuchElementException();
            return theItems[current++];
        }
    }


    @Override
    public Iterator<AnyType> iterator() {
        return new ArrayListIterator();
    }
}

可以看到将一个类的定义放在另一个类中这就是内部类。内部类可以访问外部类的私有属性,因为上面没有报错。为了测试我们给数组集合类添加add方法和构造器。(注意,这里并不是真的一个数组集合,因为不能动态改变数组的容量)

    public MyArrayList(int size) {
        // 使用泛型类型限界并且对类型数组进行转换
        theItems = (AnyType[]) new Object[size];
    }

    public void add(AnyType e) {
        if (theSize < theItems.length)
            theItems[theSize++] = e;
    }

测试:

public static void main(String[] args) {
        MyArrayList list = new MyArrayList(5);
        for (int i = 0; i < 5; i++) {
            list.add(i);
        }
        Iterator iterator = list.iterator();

        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
          //foreach内部也是使用上面这种原理
//        for (Object o : list) {
//            System.out.println(o);
//        }
    }

输出:
0
1
2
3
4

那么,为什么内部类会自动拥有对其外部类所有成员的访问权呢?

原因是当new MyArrayList(5)实例化一个外部类对象的时候,再调用iterator方法new ArrayListIterator()再实例化一个内部类对象时,这个内部类对象必定会秘密捕获一个指向那个外部类的引用。然后当内部类访问外部类的成员时,就是用的这个外部类引用。

二、内部类的使用

public MyArrayList outer(){
     return MyArrayList.this;
 }
public void main(String[] args){
  MyArrayList list = new MyArrayList(5);
  MyArrayList.ArrayListIterator iterator = list.new ArrayListIterator();
} 
所以想要创建内部类对象,必须使用外部类的对象来创建内部类,而不是使用外部类,如果你的内部类是嵌套类(静态内部类),那么它就不需要外部类对象的引用。

三、嵌套内部类

如果不希望内部类对象与外部类对象之间有联系,那么可以将内部类声明为static,想要理解static嵌套类的含义就必须记住:

class Out {
    private static int age = 12;

    static class In {
        static int age = 13;                        //3、嵌套内部类可以有static数据
        public void print() {
            int age = 14;
            System.out.println("局部变量:" + age);  //输出14
            System.out.println("内部类变量:" + this.age);    //输出13
            //下面报错,2、不能再嵌套内部类中创建非静态类对象
           // System.out.println("外部类变量:" + Out.this.age);  //输出12
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out.In();           //1、不再需要外部类对象,没有了new Out()
        in.print();
    }
}
public interface IntefaceDemo {
    void howdy();

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

        public static void main(String[] args){
            new  Test().howdy();
        }
    }
}
输出:
howdy!
class A {
    private void f() {
        System.out.println("f()");
    }

    class B {
        private void h() {
            System.out.println("h()");
        }

        class C {
            public void g() {
                f();
                h();
                System.out.println("j()");
            }
        }
    }

}

public class ABC {
    public static void main(String[] args){
        A a = new A();
        A.B b = a.new B();
        A.B.C c = b.new C();
        c.g();
    }
}
输出:
f()
h()
j()

四、私有内部类

如果一个内部类只希望被外部类中的方法操作,那么可以使用private声明内部类,下面的代码中,我们必须在Out类里面生成In类的对象进行操作,而无法再使用Out.In in = new Out().new In() 生成内部类的对象

也就是说,此时的内部类只有外部类可控制

class Out {
    private int age = 12;

    private class In {
        public void print() {
            System.out.println(age);
        }
    }
    public void outPrint() {
        new In().print();
    }
}

public class Demo {
    public static void main(String[] args) {
        //此方法无效
        /*
        Out.In in = new Out().new In();
        in.print();
        */
        Out out = new Out();
        out.outPrint();
    }
}

但在java中凡事并非绝对的,我们可以通过内部类向上转型来访问private或protect内部类。

class Out {
    private class In implements InIn {              //私有内部类实现某接口
        public void print() {
            System.out.println("你好");
        }
    }

    public InIn outPrint() {                      
        return new In();
    }
}

public class Demo {
    public static void main(String[] args) {

        InIn in = new Out().outPrint();
        in.print();

    }
}

通过这种方式可以完全组织任何依赖于类型的编码,并且完全隐藏了细节,而且也不能扩展接口中的方法。

五、方法内部类

我们也可以把内部类的定义写在方法里:

只是如果想往外部类的方法中传入参数,并且该参数会被内部类使用,那么该参数必须是final
class Out {
    private int age = 12;
 
    public void Print(final int x) {
        class In {
            public void inPrint() {
                System.out.println(x);
                System.out.println(age);
            }
        }
        new In().inPrint();
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out out = new Out();
        out.Print(3);
    }
}

六、匿名内部类

改写上面的例子,把内部类写在方法里并且使用匿名内部类,
这里需要注意的是:由于匿名内部类没有名字,所以不能写有参构造方法,只能往new Iterator(形参),这里的形参可以不需要final,因为该形参是传递给其基类的构造器的。

public Iterator<AnyType> iterator() {
        return new Iterator() {
            private int current = 0;

            @Override
            public boolean hasNext() {
                return current < theSize;
            }

            @Override
            public AnyType next() {
                if (!hasNext())
                    throw new NoSuchElementException();
                return theItems[current++];
            }
        };

    }

参考:
https://www.cnblogs.com/nerxious/archive/2013/01/24/2875649.html
Java编程思想

上一篇 下一篇

猜你喜欢

热点阅读