The definition of one class can be placed inside the definition of another class, which is called an inner class.

First, let’s look at Why we use inner classes in Java.

  • On the one hand, an inner class usually inherits from a class or implements an interface. The code of the inner class creates the objects of its enclosing class, so you can think of an inner class as providing some kind of window into the enclosing class.
  • On the other hand, because each inner class can independently inherit from an implementation, inner classes effectively implement multiple inheritance, allowing multiple non-interface types (classes or abstract classes) to be inherited.

If not for multiple inheritance, you can code in other ways, but using inner classes gives you some additional features:

  • An inner class can have multiple instances, each of which has its own state information and is independent of the information of the enclosing class object.
  • Within a single enclosing class, you can have multiple inner classes implement the same interface in different ways, or inherit from the same class.
  • The time at which the inner class object is created does not depend on the creation of the outer class object.
  • The inner class does not have a confusing “is A” relationship and is a separate entity.

Next, let’s look at how to use inner classes.

Creating an inner class

Here’s a list of the details of Java programming thinking:

The first is how to define inner classes. There’s nothing to be said for this, just putting the class definition inside the enclosing class.

How does an inner class link to an outer class

  1. When an object of an inner class is generated, there is a connection between the object and the object that made it, so it can access all the members of its outer object without any special conditions, and the inner class also has access to all the elements of its outer class.

  2. So how does an inner class have access to all the members of the outer class?

    When a peripheral class objects created an inner class object, the inner class can secretly capture a pointer to the outer class object references, then visit also surround class members, is to use the reference to select the peripheral members of the class, the compiler to complete all the details for us, so, build internal class, need a pointer to a class object references, its periphery The compiler will report an error if it cannot access the reference.

public interface Selector {
    boolean end(a);

    Object current(a);

    void next(a);
}

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(a) {
            return i == items.length;
        }

        @Override
        public Object current(a) {
            return items[i];
        }

        @Override
        public void next(a) {
            if(i < items.length){ i++; }}}public Selector selector(a){
        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.println(selector.current() +""); selector.next(); }}}Copy the code

Let’s look again at two special syntaxes for generating references to external class objects and defining inner classes

  1. If you need to generate a reference to an external class object, the class name of the external class can be followed by a dot and this. The resulting reference automatically has the correct type, 👾, which is known and checked at compile time.

  2. If you want to create an inner class object, you must provide a reference to the external class object in the expression, using the.new syntax.

Refer to the code below for usage.

public class DotThis {

    void f(a){
        System.out.println("DotThis.f()");
    }

    public class Inner{
        public DotThis outer(a){
            return DotThis.this; }}public Inner inner(a){
        return new Inner();
    }

    public static void main(String[] args) {
        DotThis dt =newDotThis(); Inner inner = dt.inner(); DotThis outer = inner.outer(); System.out.println(outer.equals(dt)); }}public class DotNew {

    public class Inner{}

    public static void main(String[] args) {
        DotNew dotNew = new DotNew();
        Inner inner = dotNew.new Inner(a); }}Copy the code

How does an inner class go up to a base class or interface to make interface implementation invisible

When an inner class is upcast to a base class or interface, the implementation of that interface can be completely invisible or even unavailable, resulting in only a reference to the base class or interface, hiding the implementation. Let’s look at the following code.

public interface Contents {
    int value(a);
}

public interface Destination {
    String readLable(a);
}

public class Parcel2 {

    private class PContents implements Contents{

        private  int i = 11;
        @Override
        public int value(a) {
            returni; }}protected class PDestination implements Destination{

        private String label;

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

        @Override
        public String readLable(a) {
            returnlabel; }}public Contents contents(a){
        return new PContents();
    }

    public  Destination destination(String s){
        return newPDestination(s); }}public class TestParcel2 {

    public static void main(String[] args) {

        Parcel2 parcel2 = new Parcel2();


        Contents contents = parcel2.contents();
        Destination dest = parcel2.destination("xxx");

// PContents inner class is private, so other classes except Parcel2 can't access it. It can't even be converted down to inner class because its name is not accessible. Implementation details are completely hidden.
// PConents conents = parcel2.new PConents();}}Copy the code

Method and inner classes in the scope

👀 First let’s look at the reasons for defining inner classes in methods and scopes

  • An inner class implements a certain type of interface, so you can create and return references to it.
  • To solve a complex problem, you want to create a class to aid your solution, but you don’t want the class to be public.

Let’s look at classes inside methods and scopes through local inner classes and anonymous inner classes

Local inner class

  1. define

Inner classes defined in a scope or method are called local inner classes

Consider the following example:

public class LocalInnerClass01 {

    public void function(a){
// if conditional scope
        if(true) {/* Local inner class In*/
            class  In{
                public void print(a){
                    System.out.println("Local inner class"); }}}// In in = new In(); IllegalSo, don't narrowly think of local inner classes as just inner classes defined in methods.Copy the code
  1. Local inner classes have several characteristics:
  • A local inner class is like a local variable of a method, so it cannot be accessed (outside the scope, method or part of the local inner class) from outside the enclosing class or other methods of the class, but this does not mean that an instance of a local inner class has the same life cycle as a local variable in the method that created it.
  • 👌 is not a class member, so it cannot have access modifiers.
  • 😔 cannot use mutable local variables in local inner classes. Access to local variables in scope must be final.
  • Member variables of the enclosing class can be accessed using the enclosing class name. This. member name. In the case of static methods, only static modified member variables can be accessed.
  • You can use the final or abstract modifier.
/** * Nesting a class within a method *@author FuPingstar
 * @date 2019/6/29 15:05
 */
public class Pracel5 {
    public Destination destination(String s){
        class PDetination implements Destination{
            private String label;
            
            private  PDetination(String whereTo){
                this.label = whereTo;
            }

            @Override
            public String readLable(a) {
                returnlabel; }}return newPDetination(s); }} The PDetination class is part of the Destination method, so the PDetination class cannot be accessed outside of destination. The PDetination class is defined in destination. This does not mean that the Destination method has finished executing and the PDetination example cannot be used.Copy the code

Anonymous inner class

Anonymous inner classes are created in the following format:
newThe parent class constructor (parameter list) | implementing an interface () {// The body part of the anonymous inner class} Anonymous inner classes must inherit a parent or implement an interface, and can implement only one parent or interface, and noneclassKeyword, passnewA reference returned by an expression is automatically cast up to a reference to the parent class.Copy the code

Anonymous inner classes can only be used once. When creating an anonymous inner class, an instance of the class is created immediately, and the class definition disappears immediately. Therefore, anonymous inner classes cannot be used repeatedly.

Anonymous inner class implementation of the Thread classpublic class Demo {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run(a) {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i + ""); }}}; t.start(); }}Copy the code
How to pass parameters to anonymous inner classes:

If the constructor of the base class needs to pass parameters, just pass the appropriate parameters to the constructor of the base class. Look at the following example:

/**
 *
 * anonymoud inner class demo
 * @author FuPingstar
 * @date2019/6/29 thus * / were
public class Parcel6 {

    public Wrapping wrapping(int x){
        return new Wrapping(x){
            public int valu1e(a){
                return super.value1() * 47; }}; }}Copy the code

In an anonymous class, if you want 🎃 to use an object defined outside of it, the compiler requires that its parameter references be final. Look at the following example:

public class Parcel7 {

    public Destination desination(final String test){
        return new Destination() {
            private String label = test;
            @Override
            public String readLable(a) {
                returnlabel; }}; }public static void main(String[] args) {
        Parcel7 parcel7 = new Parcel7();
        Destination xxx = parcel7.desination("xxx"); }}Copy the code
Anonymous inner class initialization

Anonymous inner classes have no constructor, so instance initialization can be used to achieve the effect of a constructor.

/ * * *@author FuPingstar
 * @date2019/6/29 confusion * /
public class Parcel8 {

    public Destination destination(final String dest, final float price){
        return new Destination() {
            private int cost;
// Instance initialization for each object:
            {
                cost = Math.round(price);
                if(cost > 100){
                    System.out.println("Over budeget"); }}private String label = dest;

            @Override
            public String readLable(a) {
                returnlabel; }}; }public static void main(String[] args) {
        Parcel8 parcel8 = new Parcel8();
        Destination xxx = parcel8.destination("xxx".101.395 F); }}Copy the code
Issues to be aware of when using anonymous inner classes:
  1. When using anonymous inner classes, you must inherit a class or implement an interface, and not both.
  2. Anonymous inner classes have no constructors and are instantiated to achieve the effect of constructors.
  3. Anonymous inner classes cannot have any static member variables or static methods.
  4. Anonymous inner classes are special local inner classes, so all the restrictions that apply to local inner classes also apply to anonymous inner classes.
  5. Anonymous inner classes cannot be abstract and must implement all the abstract methods of the inherited class and implemented interface.

Nested classes

An inner class is often called a nested class when it is declared static.

A normal inner class object implicitly holds a reference to the object of the enclosing class that created it. This changes when the inner class is static.

Characteristics of nested classes
  • To create an object of a nested class, you do not need an object of its enclosing class.
  • A non-static enclosing class object cannot be accessed from an object of a nested class. Based on Java syntax: Static methods cannot directly access non-static members.
  • The fields and methods of a normal inner class can only be placed on the outer layer of the class, so a normal inner class cannot have static data and static fields, nor can it contain nested classes, but nested classes can contain these things.

For details on how to use nested classes, please refer to the following code:

The following code does not have an inherited interface for nested classes, but only explores how nested classes differ from inner classespublic class NestedClassTest {

    private static int id = 111;
    private String name = "fupingstar";

    private static class NestedClass1{
        private int nid1 = 222;

        public int value(a){
            return nid1;
        }
// Nested classes cannot access enclosing class non-static member variables
// public String name(){
// return name;
/ /}
    }

    public class InnerClass1 {

        NestedClassTest getNestedClass(a){
// A non-static inner class uses the.this syntax to get a reference to the enclosing class that created it
            return NestedClassTest.this; }}protected static class NestedClass2{
        public static void f(a) {}

        static int nid2 = 333;

        static class NestedClass3 {
            public static void f(a) {}

            static int nid3 = 444; }}public static NestedClass1 getNestedClass1(a){
        return new NestedClass1();
    }

    public static NestedClass2 getNestedClass2(a){
        return new NestedClass2();
    }

    public static void main(String[] args) {
        NestedClass1 nestedClass1 = getNestedClass1();

        NestedClass2 nestedClass2 = getNestedClass2();

        NestedClassTest nestedClassTest = new NestedClassTest();

        System.out.println(nestedClass1.nid1);

// The enclosing class uses the.new keyword to create the inner class object
        InnerClass1 innerClass1 = nestedClassTest.new InnerClass1(a); System.out.println(innerClass1.getNestedClass().toString()); }}Copy the code
The class inside the interface

Nested classes can be part of an interface, and any classes placed in the interface are automatically public and static. Because classes are static, you simply place nested classes in the namespace of the interface and do not violate the rules of the interface.

One of the biggest benefits is that if you want all the different implementations of an interface to share some common code, you can use nested classes for the interface to solve this problem.

The authors of Java programming ideas recommend that each class be tested with a main method. The downside of this is that you have to carry the extra code that you’ve compiled, so you can use nested classes to place the test code. This generates a separate inner class that can be used to do the tests. Simply remove the bytecode of the inner class before the product is released and packaged.

Access a member of the enclosing class from a multi-tier nested class

An inner class can transparently access all members of its embedded outer class as many layers as it is nested.

Use the following code:

public class NestedClassTest01 {

    private void f(a) {}

    class A {
        private void g(a) {}
        public class B {
            void h(a) { g(); f(); }}}public static class TestInnerClass{
        public static void main(String[] args) {
            NestedClassTest01 nestedClassTest01 = new NestedClassTest01();

            A a = nestedClassTest01.new A(a);
            A.B b = a.new B(a); b.h(); }}}Copy the code


The end

This article basically describes the syntax and semantics of the inner class, the use of interface and inner class is a design tradeoff, I believe that readers in the future development process of the choice of these two issues will be gradually clear, can finally understand. In the next article I will continue to explore the connection between anonymous inner classes and closures, Lambda expressions.


Senior graduate who has not found internship yet!!