- Concepts of Programming Languages

Nested Classes

Instructor:

Learning Objectives

Functional features in an object-oriented language?

  • Identify nested and anonymous classes
  • Use object-oriented concepts to implement lambda expressions

"Higher-order functions" in Java 1.0

  1. public class MyArray {
  2. public int[] elems // ...
  3. public MyArray map(IntMapper m) {
  4. MyArray result = new MyArray();
  5. for (int i = 0; i < elems.length; i++) {
  6. result.elems[i] = m.apply(elems[i]);
  7. }
  8. return result;
  9. }
  10. }
  1. interface IntMapper {
  2. public int apply(int v);
  3. }
  4. class IntIncrement implements IntMapper {
  5. public int apply(int v) {
  6. return v + 1;
  7. }
  8. }
  1. public static void main(String[] args) {
  2. MyArray a = new MyArray();
  3. // fill a
  4. // passing in an instance of a named class
  5. MyArray b = a.map(new IntIncrement());
  6. }
  • Can we avoid explicit named class?

"Higher-order functions" in Java 1.1

  1. public class MyArray {
  2. public int[] elems // ...
  3. public MyArray map(IntMapper m) {
  4. MyArray result = new MyArray();
  5. for (int i = 0; i < elems.length; i++) {
  6. result.elems[i] = m.apply(elems[i]);
  7. }
  8. return result;
  9. }
  10. }
  1. interface IntMapper {
  2. public int apply(int v);
  3. }
  1. public static void main(String[] args) {
  2. MyArray a = new MyArray();
  3. // fill a
  4. // passing in an instance of an anonymous class
  5. MyArray b = a.map(new IntMapper() {
  6. public int apply(int v) { return v + 1; }
  7. });
  8. }

Higher-order functions in Java 1.8

  1. public class MyArray {
  2. public int[] elems // ...
  3. public MyArray map(IntMapper m) {
  4. MyArray result = new MyArray();
  5. for (int i = 0; i < elems.length; i++) {
  6. result.elems[i] = m.apply(elems[i]);
  7. }
  8. return result;
  9. }
  10. }
  1. @FunctionalInterface
  2. interface IntMapper {
  3. public int apply(int v);
  4. }
  1. public static void main(String[] args) {
  2. MyArray a = new MyArray();
  3. // fill a
  4. // passing in an instance of an anonymous class
  5. MyArray b = a.map(v -> v + 1);
  6. }

Java Functional Interface

  1. @FunctionalInterface
  2. interface Function<T,R> {
  3. public R apply (T x);
  4. }
  1. Function<Integer,Integer> intIncrement = new Function<> () {
  2. public Integer apply (Integer x) { return x + 1; }
  3. };
  1. Function<Integer,Integer> intIncrement = x -> x + 1;

Nonfunctional Interfaces in Java

  • Some event interfaces have multiple methods
  • Cannot use lambda notation: not clear which method is meant
  1. @FunctionalInterface
  2. interface MouseListener { /* from java.awt.event */
  3. void mouseClicked (MouseEvent e);
  4. void mouseEntered (MouseEvent e);
  5. // ...
  6. }
  1. MouseListener.java:1: error: Unexpected @FunctionalInterface annotation
  2. @FunctionalInterface
  3. ^
  4. MouseListener is not a functional interface
  5. multiple non-overriding abstract methods found in interface MouseListener

Implementing Nested Classes

  • javac (the compiler) supports nested classes
  • java (the JVM) does not
  • Compiler creates new classes with $ in name

Nested Classes

Should we allow nested classes with free variables?
What could it be useful for?
What do we have to be careful about?

Java Threads 1.8

  1. public static void main (String[] args) {
  2. for (int i = 0; i < 5; i++) {
  3. int x = i;
  4. new Thread (() -> {
  5. while (true) System.out.print (x);
  6. }).start ();
  7. }
  8. }
  1. 3331100000000000000000000000000022222222222222224444222...
  • Variable x is free in nested class (line 5)
  • With closures: no need for explicit field and constructor
  • Especially useful for GUI programming and listeners
  • Which line seems useless in the code above?

Java Threads 1.8

  1. public static void main (String[] args) {
  2. for (int i = 0; i < 5; i++) {
  3. new Thread (() -> {
  4. while (true) System.out.print (i);
  5. }).start ();
  6. }
  7. }
  • Compile error
    1. error: local variables referenced from an inner class must be final or effectively final
    2. System.out.print (i);
    3. ^

Some Languages Allow Mutable Variables

  • Python (and also Perl)
  1. import threading
  2. for i in range(5):
  3. def worker():
  4. while True:
  5. print(i, end="")
  6. threading.Thread(target=worker).start()
  1. 4444444444444444444444444444444444444444444444444444444...

Some Languages Allow Mutable Variables

  • Python
  1. import threading
  2. for i in range(5):
  3. def worker():
  4. x = i
  5. while True:
  6. print(x, end="")
  7. threading.Thread(target=worker).start()
  1. 3331100000000000000000000000000022222222222222224444222...

Some Languages Allow Mutable Variables

  • Scala
  1. for i <- (1 to 4) do
  2. new Thread (() => {
  3. while true do print (i)
  4. }).start
  1. 3331100000000000000000000000000022222222222222224444222...

Scala closure mechanism correctly treats i as effectively final in the loop body

Summary

  • Object-oriented languages support nested anonymous classes to implement functionality of restricted scope
  • Syntax resembling lambda expressions of functional languages makes such classes convenient to use
  • Classes with methods that have free variables: need closures

Java Graphics 1.8

  1. public class MyGUI extends Frame {
  2. private int count = 0;
  3. public MyGUI() {
  4. Button button = new Button ("Go"); add (button);
  5. Label out = new Label ("000", Label.CENTER); add (out);
  6. // functional interface: uses closure for count/this and out
  7. button.addActionListener (e -> {
  8. count += 1;
  9. out.setText (String.format ("%03d", count));
  10. out.repaint ();
  11. });
  12. }
  13. }

Implementing Nested Classes

  1. for (int i = 0; i < 5; i++) {
  2. int x = i;
  3. new Thread (new Runnable () {
  4. public void run () {
  5. while (true) System.out.print (x);
  6. }
  7. }).start ();
  8. }
  1. $ javac Run2.java
  2. $ javap -private 'Run2$1'
  3. Compiled from "Run2.java"
  4. final class Run2$1 implements java.lang.Runnable {
  5. final int val$x;
  6. Run2$1(int);
  7. public void run();
  8. }

Story has multiple aspects: - Avoid inventing a class name - Next: now that we have an instance of an anonymous class defined in the scope of another method, could we allow access to the variables from the environment? What could it be useful for? - Then: what do we have to be careful about? (mutable data)

- Java reference types marked as `@FunctionalInterface` > This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Graphics 1.0 ```java public class Swing1 extends Frame { int count = 0; public Swing1 () { Button button = new Button ("Go"); add (button); Label out = new Label ("000", Label.CENTER); add (out); button.addActionListener (new MyActionListener(this, out)); // set size, layout, visible etc. } } ``` ```java class MyActionListener implements ActionListener { private Swing1 parent; private Label out; public MyActionListener (Swing1 parent, Label out) { this.parent = parent; this.out = out; } public void actionPerformed (ActionEvent e) { parent.count += 1; out.setText (String.format ("%03d", parent.count)); out.repaint (); } } ``` --- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Graphics 1.1 ```java public class Swing2 extends Frame { private int count = 0; public Swing2 () { Button button = new Button ("Go"); add (button); Label out = new Label ("000", Label.CENTER); add (out); // nested anonymous class button.addActionListener (new ActionListener() { public void actionPerformed (ActionEvent e) { count += 1; out.setText (String.format ("%03d", count)); out.repaint (); } }); } } ``` --- <!-- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Threads 1.0 ```java class Run1 { public static void main (String[] args) { for (int i = 0; i < 5; i++) { new Thread (new MyRunnable (i)).start (); } } } ``` ```java class MyRunnable implements Runnable { private int x; public MyRunnable (int x) { this.x = x; } public void run () { while (true) System.out.print (x); } } ``` ```sh 3331100000000000000000000000000022222222222222224444222... ``` ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Threads 1.1 ```java class Run5 { public static void main (String[] args) { for (int i = 0; i < 5; i++) { new Thread (new Runnable () { public void run () { while (true) System.out.print (i); } }).start (); } } } ``` * Compile error ```sh Run5.java:7: error: local variables referenced from an inner class must be final or effectively final System.out.print (i); ^ ``` ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Threads 1.1 ```java class Run2 { public static void main (String[] args) { for (int i = 0; i < 5; i++) { int x = i; new Thread (new Runnable () { public void run () { while (true) System.out.print (x); } }).start (); } } } ``` ```sh 3331100000000000000000000000000022222222222222224444222... ``` ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Some Languages Allow Mutable Variables - Perl (old versions) ```perl use threads; my $i; for ($i = 0; $i < 5; $i++) { my $th = threads->create (sub { while (1) { print $i; } }); $th->detach(); } sleep (1); ``` ```sh 5555555555555555555555555555555555555555555555555555555... ``` --- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Some Languages Allow Mutable Variables - Perl (old versions) ```perl use threads; my $i; for ($i = 0; $i < 5; $i++) { my $x = $i my $th = threads->create (sub { while (1) { print $x; } }); $th->detach(); } sleep (1); ``` ```sh 3331100000000000000000000000000022222222222222224444222... ``` --- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Some Languages Allow Mutable Variables - Perl 5 ```perl use threads; my $i; for ($i = 0; $i < 5; $i++) { my $th = threads->create (sub { while (1) { print $i; } }); $th->detach(); } sleep (1); ``` ```sh 000000000000002222222222222211111111111111111133333333333333444444444444444444... ``` ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Explicit Java 1.0 implementation ```java for (int i = 0; i < 5; i++) { new Thread (new MyRunnable (i)).start (); } ``` ```java class MyRunnable implements Runnable { private int x; public MyRunnable (int x) { this.x = x; } public void run () { while (true) System.out.print (x); } } ``` ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Java Functional Interface - Java 8 introduced <i>lambda expressions</i> - How to integrate this new feature with older APIs? <div class="grid grid-cols-2 gap-4"> <div> Java Anonymous Class and Instance ```java button.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent e) { // do something on click } }); ``` </div> <div> Java Lambda Expression ```java button.addActionListener(e -> { // do something on click }); ``` </div> </div> - Java reference types marked as `@FunctionalInterface` > This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. ---

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Collections Processing - Java `map` method takes a `Function` ```java package java.util.stream; interface Stream<T> { ... // Returns a stream consisting of the results of applying // the given function to the elements of this stream <R> Stream<R> map (Function<? super T,? extends R> mapper); ... } ``` --- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Collections Processing - Provide `Stream` view of `List` in order to use map ```java public class Nested { public static void main (String[] args) { List<Integer> l = new ArrayList<> (); Stream<Integer> s = l.stream(); l.add (1); l.add (2); l.add (3); s.map (x -> x + 1) .collect (Collectors.toList ()) .forEach (x -> System.out.format ("%2d ", x)); } } ``` ```sh 2 3 4 ``` ---