Java programming ideas Chapter 10 internal classes

Posted by tranzparency on Thu, 17 Feb 2022 05:35:31 +0100

Inner class: put the definition of one class inside the definition of another class.
Advantages: organize some logically related classes together (elegant and clear), and control the visibility of internal classes.

10.1 creating internal classes

Put the definition of the class inside the peripheral class

public class Parcel1 {
	class Destination {
		private String label;
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() {
			return label;
		}
	}

	static class Destination1 {
		private String label;
		Destination1(String whereTo) {
			label = whereTo;
		}
		String readLabel() {
			return label;
		}
	}
	
	public Destination to(String s) {
		return new Destination(s);
	}
	
	public static void main(String[] args) {
		Parcel1.Destination d1 = new Parcel1().to("d1"); // The external object calls the method to get the return value of the internal object
		Parcel1.Destination d2 = new Parcel1().new Destination("d2");// Construction method of internal class of external object call
		Parcel1.Destination1 d3 = new Parcel1.Destination1("d3");// The internal class is static, which is called by the external class	
		System.out.println(d1.readLabel());
		System.out.println(d2.readLabel());
		System.out.println(d3.readLabel());
	}
}

10.2 linking to external classes

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

public class Parcel1 {
	private String label;

	class Destination {
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() {
			return label;
		}
	}

	public Destination to(String s) {
		return new Destination(s);
	}
	
	public static void main(String[] args) {
		Parcel1.Destination d1 = new Parcel1().to("d1");
		System.out.println(d1.readLabel());
	}
}

10.3 use this and new

If you need to generate a reference to an external class object, you can use the name of the external class followed by a dot and this.

public class Parcel3 {
	public void f() {
		System.out.println("Parcel3.f()");
	}
	
	public class Inner {
		public Parcel3 outer() {
			return Parcel3.this;
		}
	}
	
	public Inner inner() {
		return new Inner();
	}
	
	public static void mian(String[] args) {
		Parcel3 a = new Parcel3();
		Parcel3.Inner inner = a.inner();
		inner.outer().f();
		a.f();
	}
}

Create an internal class object using new syntax. An internal class object is implicitly connected to the external class object that created it, so you cannot create an internal class object without creating an external class object, unless it is a static internal class.

public class DotNew {
	public class Inner {}
	public static void mian(String[] args) {
		DotNew dn = new DotNew();
		DotNew.Inner dni = dn.new Inner();
	}
}

10.4 internal category and upward transformation

Internal class - the implementation of an interface can be set to private and completely invisible, which can easily hide the implementation details.

public class Parcel3 {
	public void f() {
		System.out.println("Parcel3.f()");
	}
	
	public interface ite {
		public Parcel3 outer();
	}
	
	public class Inner {
		public Parcel3 outer() {
			return Parcel3.this;
		}
	}
	
	public Inner inner() {
		return new Inner();
	}
	
	public static void mian(String[] args) {
		Parcel3 a = new Parcel3();
		ite inner = a.inner();
		inner.outer().f();
	}
}

10.5 internal classes within methods and scopes

The syntax of inner classes contains a lot of other difficult techniques

Create a complete class - a local inner class - within the scope of the method

PDestination is part of the destination() method, not part of Parcel4, so PDestination cannot be accessed outside of destination().

public class Parcel4 {
	public Destination destination(String s) {
		class PDestination implements Destination {
			private String label;
			private PDestination(String whereTo) {
				label = whereTo;
			}

			public String getLabel() {
				return label;
			}
		}
		return new PDestination(s);
	}

	public interface Destination {
		String getLabel();
	}

	public static void mian(String[] args) {
		Parcel4 p = new Parcel4();
		Parcel4.Destination p = p.destination("Tesla");
		System.out.println(d.getLabel());
	}
}

10.6 anonymous inner class

Anonymous inner classes can expand both classes and interfaces, but not both. Moreover, if an interface is implemented, only one interface can be implemented.

Use default constructor

The created class has no name. It implements the Contents interface, but it is only used once, so it is not defined outside. Specifically, an object of an anonymous class inherited from Contents is created. The reference returned through the new expression is automatically transformed upward into a reference to Contents.

interface Contents {
	public int value();
}

public class Parcel5 {
	public Contents contents() {
		return new Contents() {
			private int i = 11;
			public int value() {
				return i;
			}
		};
	}
	
	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

Use a constructor with parameters

Pass parameters to the constructor of the base class.

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

public class Parcel5 {
	public Contents contents(int x) {
		return new Contents(x) {
			public int value() {
				return super.value();
			}
		};
	}
	
	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

If an object defined outside an anonymous inner class is used, its compiler will require its parameter reference to be final

Starting with java8, you can add it by default without the final modifier. Java calls this function effective final.

interface Contents {
	public int value();
}

public class Parcel5 {
	public Contents contents(final String x) {  // Here is final
		return new Contents() {
			private String i = x;  // External objects are used here
			public String value() {
				return i;
			}
		};
	}
	
	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Contents c = p.contents("aaaaa");
		System.out.println(c.value());
	}
}

Revisit factory method

10.7 nested classes

  • If you do not need a connection between the inner class object and the outer object, you can declare the inner class as static, that is, nested class.
  • An ordinary inner class object implicitly holds a reference to the peripheral object that created it. However, when the inner class is static, it means that the creation of nested class objects does not need its peripheral objects, and non static peripheral class objects cannot be accessed from nested class objects.
  • Ordinary inner classes cannot have static fields and cannot contain nested classes. Nested classes can

Classes inside the interface

  • Normally, you can't put any code inside the interface, but nested classes can be part of the interface. Any class placed in the interface is public and static. Because the class is static, only the nested class is placed in the namespace of the interface.
  • If you want to create some common code so that different implementations of the interface are common, you can use the nested classes of the interface.
public interface Parcel5 {
	void f();
	class contents implements Parcel5 {
		public void f() {
			System.out.println("aaaa");
		}
	
		public static void main(String[] args) {
			new contents.f();
		}
	}

}
  • You can use main() as a test class in a nested class so that there is no additional code in the external class.
public class Parcel6 implements Parcel5 {
	public void f() {
		new Parcel5.contents().f();
		System.out.println("bbbbb");
	}
	
	public static class test {
		public static void main(String[] args) {
			Parcel6 p = new Parcel6();
			p.f();
		}
	}
}

Accessing members of an external class from a multi-level nested class

It doesn't matter how many layers an inner class is nested - it has transparent access to all members of all the outer classes it is embedded in

public class Parcel7 {
	class A {
		private void f() {
			System.out.println("fffff");
		}
		class B {
			class C {
				void h() { 
					f();
				}
			}
		}
	}

	public static class D {
		public static void main(String[] args) {
			Parcel7 a = new Parcel7();
			Parcel7.A a1 = a.new A();
			Parcel7.A.B B = a1.new B();
			Parcel7.A.B.C C = b.new C();
			c.h();
		}
	} 
}

10.8 why internal classes are needed

Inner classes make the solution of multiple inheritance complete. The interface solves some problems, and the internal class effectively implements "multiple inheritance". That is, inner classes allow you to inherit multiple non interface types (classes or abstract classes).

Implement two interfaces in one class

You can use a single class or an inner class.

interface A {
	void f1();
}

interface B {
	void f2();
}

class X implements A, B {
	public void f1() {
		System.out.println("X...f1...");
	}
	
	public void f2() {
		System.out.println("X...f2...");
	}
}

class Y implements A {
	public void f1() {
		System.out.println("Y...f1...");
	}
	
	B makeB {
		return new B() {
			public void f2() {
				System.out.println("X...f2...");
			}
		}
	}
}

public class Parcel9 {
	public static void main(String[] args) {
		X x = new X();
		x.f1();
		x.f2();
		Y y = new Y();
		B b = y.makeb();
		y.f1();
		b.f2();
	}
}

Implement two concrete or abstract classes in one class

Multiple inheritance can only be achieved using inner classes.

class D {
	void f1() {
		System.out.println("f1...");
	}
}

abstract class E {
	abstract void f2();
}

class Z extends D {
	E makeE {
		return new E() {
			void f2() {
				System.out.println("f2...");
			}
		}
	}
}

public class Parcel10 {
	public static void main(String[] args) {
		Z z = new Z();
		z.f1();
		E e = z.makeE();
		e.f2();
	}
}

Closures and callbacks

A closure is a callable object that records information from the scope in which it was created. Through this definition, we can see that the internal class is an object-oriented closure, because it not only contains the peripheral class object (the scope information of creating the internal class), but also automatically has a reference to the external class. Within this scope, the internal class has the right to operate all members, including private members.

Callback: the object can carry some information that allows it to call the initial object at a later time. The Java language does not contain pointers. The function of providing closures through internal classes is an excellent solution, which is more flexible and secure than pointers.

interface AAA {
	void f();
}

class BBB {
	void f() {
		System.out.println("B...");
	}
}

public class Parcel12 extends BBB implements AAA {
	public void f() {
		super.f();
		System.out.println("P...");
	}
	
	public class CCC {
		public void f() {
			Parcel12.this.f();
		}
	}

	public static class DDD {
		public static void main(String[] args) {
			Parcel12 p = new Parcel12();
			Parcel12.CCC ccc = p.new CCC();
			ccc.f();
		}
	}
}

Internal classes and control framework

Light and Water are members of external classes and the hardware of the system. Using internal classes, you can access members of external classes at will.

public abstract class Event {
	private long eventTime;
	protected final long delayTime;
	public Event(long delayTime) {
		this.delayTime = delayTime;
		start();
	}

	public void start() {
		eventTime = System.nanoTime() >= delayTime;
	}

	public boolean ready() {
		return System.nanoTime() >= eventTime;
	}
	
	public abstract void action();
}
public class Controller {
	private List<Event> eventList = new ArrayList<Event>();
	public void addEvent(Event c) {
		eventList.add(c);
	}
	public void run() {
		while (eventList.size() > 0) {
			// Use for (event E: eventlist) to report Java util. Concurrentmodificationexception error
			// https://zhuanlan.zhihu.com/p/85359247
			for (Event e : new ArrayList<Event>(eventList)) {
				if (e.ready()) {
					System.out.println(e);
					e.action();
					eventList.remove(e);
				}
			}
		}
	}
}
public class GreenhouseControls extends Controller{
	private boolean light = false;
	private boolean water = false;
	private String thermostat = "Day";
	//Light on event
	public class LightOn extends Event{
		public LightOn(long delayTime) {
			super(delayTime);
		}
		public void action() {
			light = true;
		} 
		public String toString(){
			return "Light is on";
		}
	}
	//Light off event
	public class LightOff extends Event{
		public LightOff(long delayTime) {
			super(delayTime);
		}
		public void action() {
			light = false;
		} 
		public String toString(){
			return "Light is off";
		}
	}
	//Boiling water event
	public class WaterOn extends Event{
		public WaterOn(long delayTime) {
			super(delayTime);
		}
		public void action() {
			water = true;
		} 
		public String toString(){
			return "Water is on";
		}
	}
	//Water shutdown event
	public class WaterOff extends Event{
		public WaterOff(long delayTime) {
			super(delayTime);
		}
		public void action() {
			water = false;
		} 
		public String toString(){
			return "Water is off";
		}
	}
	//Temperature regulation event at night
	public class ThermostatNight extends Event{
		public ThermostatNight(long delayTime) {
			super(delayTime);
		}
		public void action() {
			thermostat = "Night";
		} 
		public String toString(){
			return "Therostat on night setting";
		}
	}
	//Temperature regulation during daytime events
	public class ThermostatDay extends Event{
		public ThermostatDay(long delayTime) {
			super(delayTime);
		}
		public void action() {
			thermostat = "Day";
		} 
		public String toString(){
			return "Therostat on day setting";
		}
	}
	//Ring event
	public class Bell extends Event{
		public Bell(long delayTime) {
			super(delayTime);
		}
		//After the bell rings, add a new Bell(delayTime) to the eventList
		public void action() {
			addEvent(new Bell(delayTime));
		} 
		public String toString(){
			return "Bing!";
		}
	}
	//Restart event
	public class Restart extends Event{
		private Event[] eventList;
		public Restart(long delayTime, Event[] eventList) {
			super(delayTime);
			this.eventList = eventList;
			for(int i = 0; i < eventList.length; i++){
				addEvent(eventList[i]);
			}
		}
		public void action() {
			for(int i = 0; i < eventList.length; i++){
				eventList[i].start();//Restart each event.
				addEvent(eventList[i]);
			}
			start();//Start the current Restart event
			addEvent(this);//Add the current Restart event to the eventList and start the loop.
		}
		public String toString(){
			return "Restarting system!";
		}
	}
	//Termination event
	public class Terminate extends Event{
		public Terminate(long delayTime) {
			super(delayTime);
		}
		public void action() {
			System.exit(0);
		} 
		public String toString(){
			return "Terminating!";
		}
	}
}
public class GreenhouseController {
	public static void main(String[] args) {
		GreenhouseControls gc = new GreenhouseControls();
		gc.addEvent(gc.new Bell(900));
		Event[] eventList = {
			gc.new ThermostatNight(0),
			gc.new LightOn(200),
			gc.new LightOff(400),
			gc.new WaterOn(600),
			gc.new WaterOff(800),
			gc.new ThermostatDay(1400)
		};
		gc.addEvent(gc.new Restart(2000, eventList));
		if(args.length == 0){
			gc.addEvent(gc.new Terminate(5000));
		}
		gc.run();
	}
}

10.9 inheritance of internal classes

The internal class contains an external class object as a member variable by default. The member variable is initialized by passing in the external class object in the construction method of the internal class. Subclass initialization needs to call the constructor of the inner class, but the inner class has no parameterless constructor, so it needs to be specified, that is, the outer class super();

public class G1 {
	public class G2 {
	}
}
public class G3 extends G1.G2 {
	G3(G1 g1) {
		g1.super()'
	}
}

Syntax summary of super and this

super is the parent object and this is the current object

  • super(delayTime); Call the constructor of the parent class
  • super.f(); Call the method of the parent class
  • this.delayTime; Member variables of the current object
  • addEvent(this); Current object
  • Parcel3.this; References to external classes

10.10 can internal classes be overridden

If you create an inner class, then inherit its outer class and redefine the inner class, can the inner class be overridden?
When inheriting a peripheral class, the internal class does not change. The two internal classes are completely independent entities, each in its own namespace.
You can let the inner class of the child class inherit the inner class of the parent class.

10.11 local internal class

Comparison between local inner class and anonymous inner class
Advantages of local inner class: you can get objects of more than one inner class. A constructor needs to be overloaded or a named constructor is required.

10.12 internal class identifier

Out.java Obtained after file compilation Out.class,Out$Inner1.class,Out$Inner2.class,Out$Inner3.class
Local internal class, the compiler will $"Add a numeric number Find.class,LocalClass$1FindImpl.class LocalClass$2FindImpl.class,LocalClass.class
Anonymous inner class, the compiler simply produces a number as its identifier AClass$1.class,AClass$2.class ,AClass.class,Find.class
The inner class is nested in other inner classes. You only need to add the name directly to its peripheral class identifier and“ $"Behind Mulit.class Mulit$One.class,Mulit$One$Two.class,Mulit$One$Two$Three.class

https://blog.csdn.net/weixin_36210698/article/details/73550679

Topics: Java Back-end