Java local inner classes, anonymous inner classes Detailed

External and internal class Class

As we all know, each file must have an external java class with the java file of the same name, this external class can be public or default of. In addition to the external class with the same name, you may have the same name as the class with other external flat outer class level, but they must be the default.

And then define inner class refers to a class inside the outer class.

Classification inner class

We are talking about inner classes, it is the official name for nested class (Nested Classes). Nested class including a static inner classes (Static Nested Classes) and an internal class (Inner Classes). The interior is divided into categories member inner classes, local inner classes (Local Classes) and anonymous inner classes (Anonymous Classes).

Here Insert Picture Description
But as I understand the point of view, should such a drawing:
Here Insert Picture Description
From the use of terms, we should go with a static inner classes and members of inner classes, because both are through outer.staticInneror outer.memberInnerthis .syntax to use the class name. In contrast, partial inner and anonymous inner classes can not pass .to the class name syntax, because the use of both will be limited to within a scope of (i.e. in a pair {...}among braces), this is because the two are not as members of the outer class (neither a static member, nor is it an instance member).

  • When the object is a non-static inner class is created, it will depend on the external object (hold the external object reference). But specifically it refers to non-static context in this context. More specifically, members of the class will be held inside the outer class object reference, local inner classes (named or anonymous) but not necessarily.
  • Inner class can access private methods outside the class, and private members.
  • 局部内部类不管是具名还是匿名的,其类定义前不可以加上static(究其原因是局部内部类不属于外部类的成员,只有成员才有静态和非静态之分)。同样,其成员和方法也不可以加上static
  • 局部内部类包括匿名内部类,即局部内部类中分别两种:具名的、匿名的。匿名的就叫匿名内部类了。
  • 具名的局部内部类的类定义不可以加上访问权限限定词。public protected private都不可以。

局部内部类

局部内部类分为两种:1.通过继承抽象类或实现接口或普通类来定义新类(将用这种举例,因为这种更具有实用价值) 2.直接定义一个新类(虽然感觉这样做没什么用)。如果是匿名内部类则只有第1种。

首先给一个interface:

public interface Destination {
  String readLabel();
}

局部内部类访问外部类成员(non-static context)

当局部内部类的类定义处于一个非静态上下文时,局部内部类可以访问外部类的私有成员(静态或非静态)或私有方法(静态或非静态):(对于具名的还是匿名的都成立)

public class test3 {
    private int num = 1;
    private static int snum = 2;
    private String returnString(){
        return "outer string";
    }
    private static String sreturnString(){
        return "static outer string";
    }
    public Destination destination() {//这里是非静态方法,属于非静态context
        class PDestination implements Destination {
            private String label;
            //内部类访问外部类私有成员,这里改成snum也可以
            private int i = num;
            private PDestination() {
            	//内部类访问外部类私有方法,这里改成sreturnString也可以
                label = returnString();
            }
            public String readLabel() { return label; }
        }
        return new PDestination();
    }
    public static void main(String[] args) {
        test3 t = new test3();
        Destination d = t.destination();
    }
}

局部内部类访问外部类成员(static context)

public class test3 {
    private int num = 1;
    private static int snum = 2;
    private String returnString(){
        return "outer string";
    }
    private static String sreturnString(){
        return "static outer string";
    }

    public static void testNamed(){//静态方法内,具名的局部内部类
        class PDestination implements Destination {
            private String label = "named inner";
            public String readLabel() { return label; }
            public void test(){
                sreturnString();
                //non-static method returnString() cant be refenrenced from a static context
                //returnString();//无法通过编译
            }
        }
    }

    public static void testNoNamed() {//静态方法内,匿名内部类
        Destination d = new Destination(){
            private String label = "no named inner";
            public String readLabel() { return label; }
            public void test(){
                sreturnString();
                //non-static method returnString() cant be refenrenced from a static content
                //returnString();//无法通过编译
            }
        };
    }
    
    public static Destination d = new Destination(){//作为静态成员,的匿名内部类
        private String label = "no named inner";
        public String readLabel() { return label; }
        public void test(){
            sreturnString();
            //non-static method returnString() cant be refenrenced from a static content
            //returnString();//无法通过编译
        }        
    };
    
    public static void main(String[] args) {
        test3 t = new test3();
    }
}

经过测试发现,当局部内部类(具名或匿名)的类定义属于static context时,是不可以访问外部类的非静态成员或方法。道理很简单,static context是类相关的,此时可能没有对象,当然不能访问非静态的东西,也就是说,这种情况下局部内部类对象没有持有外部类对象的引用。上面测试了三种static context的情况:

  • 静态方法内,具名的局部内部类
  • 静态方法内,匿名内部类
  • 作为静态成员,的匿名内部类

外部类访问局部内部类成员

在局部内部类的作用域内(指包裹内部类的那个{...}内),内部类之外,是可以随意访问内部类对象的私有成员的。(注意,构造器可以直接访问;但方法和成员则必须通过内部类对象的.语法来访问哦;但一旦出了作用域,除了接口规定好的方法外,其他的方法和成员都无法访问,因为编译器只认为对象是一个Destination对象):(对于具名的还是匿名的都成立)

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 String getLabel(String s) {
        class PDestination implements Destination {
            private String label;
            private PDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel() { return label; }
        }
        //访问了内部类对象的私有成员
        return new PDestination(s).label;
    }
    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
        //System.out.println(d.label); cant resolve symbol label
        //new PDestination("");
        System.out.println(p.getLabel("private string"));
    }
}

给内部类成员赋值

当然,上面例子也可以改成,方法参数直接给内部类成员赋值:

public class Parcel5 {
    public Destination destination(String s) {
         class PDestination implements Destination {
            //private String label = s; //定义字段时同时初始化
            //也可以把下面四行注释掉,上面这行注释取消掉
            private String label;
            PDestination(){
                label = s;
            }
            public String readLabel() { return label; }
        }
        return new PDestination();
    }
    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
    }
}

局部内部类还可以访问局部变量,在jdk1.8以前这个局部变量必须加上final,由于本人jdk是1.8,新特性支持不需要加final了(注意形参也是局部变量,所以上面的例子也不需要加final)(虽然不用加final,但编译器会自动加上只是我们看不见,以保证变量初始化以后不会变,但是通常最好加上 final 作为一种暗示)。(对于具名的还是匿名的都成立)

public class Parcel5 {
    public Destination destination(String s) {
         int temp = 123;//局部变量
         class PDestination implements Destination {
            private String label = s;
            private int num = temp;//访问局部变量
            public String readLabel() {
                return label;
            }
        }
        return new PDestination();
    }
    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
        System.out.println(d.readLabel());
    }
} 

匿名内部类

使用匿名内部类的情况只能是:创建的匿名内部类继承某个类。

  1. 继承一个外部类,或静态内部类,或成员内部类,都可以。
  2. 继承一个抽象类abstract class。
  3. 实现一个接口interface。

然后通过new表达式返回的引用被自动向上转型为对其父类的引用。既然是父类引用指向子类对象,只不过这个子类有点特殊,它没有名字。所以其父类被当做“接口”使用:

  1. 如果是普通类,那么通过覆盖非静态方法,可以实现多态。
  2. 如果是抽象类,那么实现其抽象方法。
  3. 如果是接口,那么实现其所有方法。

当获得匿名内部类对象后,只能调用其父类已经定义好的方法,所以才说其父类被当做“接口”使用。

简单示例如下:

public interface Contents {
  int value();
}
public class Parcel7 {
  public Contents contents() {
    return new Contents() { // 调用父类的默认构造器
      private int i = 11;
      public int value() { return i; }
    }; // 需要分号
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents();
  }
}

在上面例子中调用了父类的默认构造器,如果父类的构造器是有参数的:

public class Wrapping {
  private int i;
  public Wrapping(int x) { i = x; }//父类的有参构造器
  public int value() { return i; }
}
public class Parcel8 {
  public Wrapping wrapping(int x) {
    // Base constructor call:
    return new Wrapping(x) { // 直接传递这个参数给父类的有参构造器
      public int value() {
        return super.value() * 47;//调用父类的value方法
      }
    }; 
  }
  public static void main(String[] args) {
    Parcel8 p = new Parcel8();
    Wrapping w = p.wrapping(10);
  }
}

由于匿名内部类没有名字,自然也没有构造器了。但可以通过实例初始化块来起到构造器的作用:

public class Parcel10 {
    public Destination destination(String dest, float price) {
        return new Destination() {
            private int cost;
            // Instance initialization for each object 这里就是实例初始化块
            {
                cost = Math.round(price);
                if(cost > 100)
                    System.out.println("Over budget!");
            }
            private String label = dest;
            public String readLabel() { return label; }
        };
    }
    public static void main(String[] args) {
        Parcel10 p = new Parcel10();
        Destination d = p.destination("Tasmania", 101.395F);
    }
}

局部内部类、匿名内部类比较

  • 匿名内部类没有名字,也就没有构造器,只有通过实例初始化块。
  • Compared named local inner classes, anonymous inner classes is somewhat limited, because only inherit or implement a class or interface.
  • When we need a named constructor or need to override the constructor (naturally need to override the constructor name), you need to use a local inner class named.
  • When we first need to define the scope, create an object several times, you need to use a local inner class named.
Published 171 original articles · won praise 130 · views 280 000 +

Guess you like

Origin blog.csdn.net/anlian523/article/details/99709973