Top Banner
Automatic Migration of Legacy Automatic Migration of Legacy Java Method Implementations Java Method Implementations to Interfaces to Interfaces Work in Progress Work in Progress RaKhatchadourian Computer Systems Technology New York City College of Technology City University of New York Department of Computer Science College of Staten Island City University of New York June 12, 2015 Based on slides by Duarte Duarte, Eduardo Martins , Miguel Marques and Ruben Cordeiro and Horstmann, Cay S. (2014-01-10). Java SE8 for the Really Impatient: A Short Course on the Basics (Java Series). Pearson Education.
61

Automatic Migration of Legacy Java Method Implementations to Interfaces

Apr 07, 2017

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Automatic Migration of Legacy Java Method Implementations to Interfaces

Automatic Migration of LegacyAutomatic Migration of LegacyJava Method ImplementationsJava Method Implementations

to Interfacesto InterfacesWork in ProgressWork in Progress

Raffi KhatchadourianComputer Systems Technology

New York City College of TechnologyCity University of New York

Department of Computer ScienceCollege of Staten Island

City University of New York

June 12, 2015

Based on slides by Duarte Duarte, Eduardo Martins, Miguel Marques andRuben Cordeiro and Horstmann, Cay S. (2014-01-10). Java SE8 for theReally Impatient: A Short Course on the Basics (Java Series). Pearson

Education.

Page 2: Automatic Migration of Legacy Java Method Implementations to Interfaces

Some demonstration code at: .http://github.com/khatchad/java8-demo

Page 3: Automatic Migration of Legacy Java Method Implementations to Interfaces

Some HistorySome History

Java was invented in the 90's as an Object-Oriented language.Largest change was in ~2005 with Java 5.

Generics.Enhanced for loops.Annotations.Type-safe enumerations.Concurrency enhancements (AtomicInteger).

Page 4: Automatic Migration of Legacy Java Method Implementations to Interfaces

Java 8 is MassiveJava 8 is Massive

10 years later, Java 8 is packed with new features.Core changes are to incorporate functional language features.

Page 5: Automatic Migration of Legacy Java Method Implementations to Interfaces

Functional LanguagesFunctional Languages

Declarative ("what not how").The only state is held in parameters.Traditionally popular in academia and AI.Well-suited for event-driven/concurrent ("reactive") programs.

Page 6: Automatic Migration of Legacy Java Method Implementations to Interfaces

Lambda ExpressionsLambda Expressions

A block of code that you can pass around so it can beexecuted later, once or multiple times.

Anonymous methods.Reduce verbosity caused by anonymous classes.

Page 7: Automatic Migration of Legacy Java Method Implementations to Interfaces

How are they different from Java methods?

Page 8: Automatic Migration of Legacy Java Method Implementations to Interfaces

LambdasLambdas(int x, int y) -> x + y

() -> 42

(String s) -> {System.out.println(s);}

() -> {return "Hello";}

Page 9: Automatic Migration of Legacy Java Method Implementations to Interfaces

LambdasLambdasExamplesExamples

BeforeBefore

Button btn = new Button();final PrintStream pStream = ...;btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { pStream.println("Button Clicked!"); }});

AfterAfter

Button btn = new Button();final PrintStream pStream = ...;btn.setOnAction(e -> pStream.println("Button Clicked!"));

Page 10: Automatic Migration of Legacy Java Method Implementations to Interfaces

LambdasLambdasExamplesExamples

List<String> strs = ...;Collections.sort(strs, (s1, s2) -> Integer.compare(s1.length(), s2.length()));

new Thread(() -> { connectToService(); sendNotification();}).start();

Page 11: Automatic Migration of Legacy Java Method Implementations to Interfaces

Functional InterfacesFunctional InterfacesSingle Abstract Method Type

Page 12: Automatic Migration of Legacy Java Method Implementations to Interfaces

Functional InterfacesFunctional InterfacesExampleExample

@FunctionalInterfacepublic interface Runnable { public void run();}

Runnable r = () -> System.out.println("Hello World!");

@FunctionalInterface:May be omitted.Generates an error when there is more than one abstract method.

Can store a lambda expression in a variable.Can return a lambda expression.

Page 13: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.functionjava.util.functionPredicate<T> - a boolean-valued property of an objectConsumer<T> - an action to be performed on an objectFunction<T,R> - a function transforming a T to a RSupplier<T> - provide an instance of a T (such as a factory)UnaryOperator<T> - a function from T to TBinaryOperator<T> - a function from (T,T) to T

Page 14: Automatic Migration of Legacy Java Method Implementations to Interfaces

Method ReferencesMethod ReferencesTreating an existing method as an instance of a

Functional Interface

Page 15: Automatic Migration of Legacy Java Method Implementations to Interfaces

Method ReferencesMethod ReferencesExamplesExamples

class Person { private String name; private int age;

public int getAge() {return this.age;} public String getName() {return this.name;}}

Person[] people = ...;Comparator<Person> byName = Comparator.comparing(Person::getName);Arrays.sort(people, byName);

Page 16: Automatic Migration of Legacy Java Method Implementations to Interfaces

Method ReferencesMethod ReferencesKinds of method referencesKinds of method references

A static method (ClassName::methName)An instance method of a particular object(instanceRef::methName)A super method of a particular object (super::methName)An instance method of an arbitrary object of a particular type(ClassName::methName)A class constructor reference (ClassName::new)An array constructor reference (TypeName[]::new)

Page 17: Automatic Migration of Legacy Java Method Implementations to Interfaces

Method ReferencesMethod ReferencesMore ExamplesMore Examples

Consumer<Integer> b1 = System::exit;Consumer<String[]> b2 = Arrays::sort;Consumer<String[]> b3 = MyProgram::main;

Page 18: Automatic Migration of Legacy Java Method Implementations to Interfaces

Default MethodsDefault MethodsAdd default behaviors to interfaces

Page 19: Automatic Migration of Legacy Java Method Implementations to Interfaces

Why default methods?Why default methods?Java 8 has lambda expressions. We want to start using them:

List<?> list = ...list.forEach(...); // lambda code goes here

Page 20: Automatic Migration of Legacy Java Method Implementations to Interfaces

There's a problemThere's a problemThe forEach method isn’t declared by java.util.List nor thejava.util.Collection interface because doing so would break

existing implementations.

Page 21: Automatic Migration of Legacy Java Method Implementations to Interfaces

Default MethodsDefault MethodsWe have lambdas, but we can't force new behaviors into current

libraries.

Solution: default methods.

Page 22: Automatic Migration of Legacy Java Method Implementations to Interfaces

Default MethodsDefault MethodsTraditionally, interfaces can't have method definitions (justdeclarations).Default methods supply default implementations of interfacemethods.By default, implementers will receive this implementation if they don'tprovide their own.

Page 23: Automatic Migration of Legacy Java Method Implementations to Interfaces

ExamplesExamples

Page 24: Automatic Migration of Legacy Java Method Implementations to Interfaces

Example 1Example 1BasicsBasics

Page 25: Automatic Migration of Legacy Java Method Implementations to Interfaces

public interface A {

default void foo() { System.out.println("Calling A.foo()"); }}

public class Clazz implements A {}

Page 26: Automatic Migration of Legacy Java Method Implementations to Interfaces

Client codeClient code

Clazz clazz = new Clazz();clazz.foo();

OutputOutput

"Calling A.foo()"

Page 27: Automatic Migration of Legacy Java Method Implementations to Interfaces

Example 2Example 2Getting messyGetting messy

Page 28: Automatic Migration of Legacy Java Method Implementations to Interfaces

public interface A {

default void foo(){ System.out.println("Calling A.foo()"); }}

public interface B {

default void foo(){ System.out.println("Calling B.foo()"); }

}

public class Clazz implements A, B {}

Does this code compile?

Page 29: Automatic Migration of Legacy Java Method Implementations to Interfaces

Of course not (Java is not C++)!

class Clazz inherits defaults for foo() from both typesA and B

How do we fix it?

Page 30: Automatic Migration of Legacy Java Method Implementations to Interfaces

Option A:

public class Clazz implements A, B { public void foo(){/* ... */}}

We resolve it manually by overriding the conflicting method.

Option B:

public class Clazz implements A, B { public void foo(){ A.super.foo(); // or B.super.foo() }}

We call the default implementation of method foo() from eitherinterface A or B instead of implementing our own.

Page 31: Automatic Migration of Legacy Java Method Implementations to Interfaces

Going back to the example of forEach method, how can we force it'sdefault implementation on all of the iterable collections?

Page 32: Automatic Migration of Legacy Java Method Implementations to Interfaces

Let's take a look at the Java UML for all the iterable Collections:

To add a default behavior to all the iterable collections, a defaultforEach method was added to the Iterable<E> interface.

Page 33: Automatic Migration of Legacy Java Method Implementations to Interfaces

We can find its default implementation in java.lang.Iterableinterface:

@FunctionalInterfacepublic interface Iterable { Iterator iterator(); default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }}

Page 34: Automatic Migration of Legacy Java Method Implementations to Interfaces

The forEach method takes a java.util.function.Consumerfunctional interface type as a parameter, which enables us to pass in a

lambda or a method reference as follows:

List<?> list = ...list.forEach(System.out::println);

This is also valid for Sets and Queues, for example, since both classesimplement the Iterable interface.Specific collections, e.g., UnmodifiableCollection, may overridethe default implementation.

Page 35: Automatic Migration of Legacy Java Method Implementations to Interfaces

SummarySummaryDefault methods can be seen as a bridge between lambdas and JDKlibraries.Can be used in interfaces to provide default implementations ofotherwise abstract methods.

Clients can optionally implement (override) them.Can eliminate the need for skeletal implementators like

.Can eliminate the need for utility classes like .

static methods are now also allowed in interfaces.

AbstractCollectionCollections

Page 36: Automatic Migration of Legacy Java Method Implementations to Interfaces

Open ProblemsOpen ProblemsCan eliminate the need for skeletal implementators like

.AbstractCollection

Page 37: Automatic Migration of Legacy Java Method Implementations to Interfaces

MotivationMotivationTwo cases:

1. Complete migration of skeletal implementation to interface.Makes type hierarchy less complicated.

One less type.Makes interfaces easier to implement.

No global analysis required to find skeletal implemention.Makes interfaces easier to maintain.

No simultaneous modifications between interface and skeletalimplementation required.

2. Partial migration of skeletal implementation to interface.Possibly makes interfaces easier to implement.

All implementations may not need to extended the skeletalimplementation.

Possibly makes interfaces easier to maintain.Possibily less simultaneous modifications between interfaceand skeletal implementation required.

Page 38: Automatic Migration of Legacy Java Method Implementations to Interfaces

Open ProblemsOpen ProblemsCan eliminate the need for utility classes like .Collections

Page 39: Automatic Migration of Legacy Java Method Implementations to Interfaces

MotivationMotivationTwo cases:

1. Complete migration of utility classes to interface(s).Makes type hierarchy less complicated.

One less type.Makes interfaces easier to use.

No global analysis required to find utility class.Only methods applicable to the interface are present.Can call methods like instance methods (a.b() instead of b(a)).IDE will show applicable methods.

2. Partial migration of utility classes to interface(s).Still makes interfaces easier to use for certain methods.Can avoid global analysis in some cases.

Page 40: Automatic Migration of Legacy Java Method Implementations to Interfaces

Open ProblemsOpen ProblemsCan eliminate the need for skeletal implementators like

.AbstractCollection

Can we automate this?Can we automate this?1. How do we determine if an interface

implementor is "skeletal?"2. What is the criteria for an instance method to be

converted to a default method?3. What if there is a hierarchy of skeletal

implementors, e.g., AbstractCollection,AbstractList?

4. How do we preserve semantics?5. What client changes are necessary?

Page 41: Automatic Migration of Legacy Java Method Implementations to Interfaces

Skeletal ImplementatorsSkeletal ImplementatorsAbstract classes that provide skeletal implementations

for interfaces.

Let's take a look at the Java UML for AbstractCollection:

Page 42: Automatic Migration of Legacy Java Method Implementations to Interfaces

Open ProblemsOpen ProblemsCan eliminate the need for utility classes like .Collections

Can we automate this?Can we automate this?1. How do we determine if a class is a

"utility" class?2. What is the criteria for a static method to

be:Converted to an instance method?Moved to an interface?

3. How do we preserve semantics?4. What client changes are necessary?

Page 43: Automatic Migration of Legacy Java Method Implementations to Interfaces

Let's start with question #2 for both the previous slides. There seems tobe a few cases here:

# From a class To an interface1. instance method default method2. static method default method3. static method static method

Is case #3 a simple move method refactoring? (Yesbut to where? -- more later)

Page 44: Automatic Migration of Legacy Java Method Implementations to Interfaces

Why Is This Complicated?Why Is This Complicated?Case #1 corresponds to moving a skeletal implementation to an

interface.

# From a class To an interface1. instance method default method

For case #1, can we not simply use a move method refactoring?

No. The inherent problem is that, unlike classes,interfaces may extend multiple interfaces. As such, wenow have a kind of multiple inheritance problem to

deal with.

Page 45: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #1Case #1Some (perhaps) Obvious PreconditionsSome (perhaps) Obvious Preconditions

Source instance method must not access any instance fields.Interfaces may not have instance fields.

Can't currently have a default method in the target interface withthe same signature.Can't modify target method's visibility.

Some Obvious TasksSome Obvious TasksMust replace the target method in the interface (add a body to it).Must add the default keyword.Must remove any @Override annotations (it's top-level now).If nothing left in abstract class, remove it?Need to deal with documentation.

Page 46: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #1Case #1Some Not-so-obvious PreconditionsSome Not-so-obvious Preconditions

Suppose we have the following situation:

interface I { void m();}

interface J { void m();}

abstract class A implements I, J { @Override void m() {...}}

Here, A provides a partial implementation of I.m(), which also happensto be declared as part of interface J.

Page 47: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #1Case #1Some Not-so-obvious PreconditionsSome Not-so-obvious Preconditions

Now, we pull up A.m() to be a default method of I:

interface I { default void m() {..} //was void m();}

interface J { void m(); //stays the same.}

abstract class A implements I, J { //now empty, was: void m() {...}}

We now have a compile-time error in A because A needs to declarewhich m() it inherits.In general, inheritance hierarchies may be large and complicated.Other preconditions may also exist (work in progress).

Page 48: Automatic Migration of Legacy Java Method Implementations to Interfaces

Why Is This Complicated?Why Is This Complicated?Case #3 corresponds to moving a static utility method to an interface.

# From a class To an interface3. static method default method

For case #1, can we not simply use a move method refactoring?

No and for a few reasons:

Method signature must change (not only removingthe static keyword).Client code must change from static method callsto instance method calls.Which parameter is to be the receiver of the call?What is the target interface?

Page 49: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #3Case #3Some (perhaps) Obvious PreconditionsSome (perhaps) Obvious Preconditions

The source method vaciously doesn't access instance fields.Default version of the source method can't already exist in the targetinterface.

Some Obvious TasksSome Obvious TasksMust add the default keyword.If nothing left in utility class, remove it?Need to deal with documentation.

Page 50: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #3Case #3Some Not-so-obvious PreconditionsSome Not-so-obvious Preconditions

/** * Copies all of the elements from one list into another. */public static <T> void Collections.copy(List<? super T> dest, List<? extends T> src);

Should this be a static or default method?Call it as Collections.copy(l2, l1) or l2.copy(l1)?

It's probably more natural to leave it as static .Also, we can't express the generics if it was refactored to adefault method.

The generics here express the relationship between the twolists in a single method.We can't do if it was default.

Page 51: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #3Case #3You may be tempted to move the copy method to a default method inList, but since the method uses generics and expresses a very distinct

relationship between two two Lists, you will lose the type safetyoffered by the generics. For example, suppose you have:

List<String> l1;List<String> l2;

Now, you copy the contents of l2 into l1:

Collections.copy(l1, l2);

If l2 is, instead, List<Integer>, you will receive a compile-time error.However, if copy was a default method of, say, the List interface, we

would have something like this:

l1.copy(l2)

Making l2 a List<Integer> would not result in a compile-time error.

Page 52: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #3Case #3This method should actually move to the List interface and remainstatic, but:

What is there's another method named copy in the List interface?List is an interface, and interfaces can extend other interfaces.

What if there's another method with the same signature in thehierarchy?

Page 53: Automatic Migration of Legacy Java Method Implementations to Interfaces

Case #4Case #4Determining the target interface for caseDetermining the target interface for case

#4#4We can apply a heuristic: take the least common interface of the

parameters. For example:

public static void m(I1 p1, I2 p2, ..., In pn);

Here, we would take the closest sibling to I1, I2, ..., In as the targetinterface. Otherwise, we normally take the first parameter if it's an

interface.

Page 54: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamfilter/map/reduce for Java

Page 55: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamList<Student> students = ...;Stream stream = students.stream(); // sequential version

// parallel versionStream parallelStream = students.parallelStream();

traversed onceinfinitelazy

Page 56: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamStream sourcesStream sources

CollectionsCollections

Set<Student> set = new LinkedHashSet<>();Stream<Student> stream = set.stream();

GeneratorsGenerators

Random random = new Random();Stream<Integer> randomNumbers = Stream.generate(random::nextInt);// or simply ...IntStream moreRandomNumbers = random.ints();

From other streamsFrom other streams

Stream newStream = Stream.concat(stream, randomNumbers);

Page 57: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamIntermediate operationsIntermediate operations

.filter - excludes all elements that don’t match a Predicate

.map - perform transformation of elements using a Function

.flatMap - transform each element into zero or more elements byway of another Stream.peek - performs some action on each element.distinct - excludes all duplicate elements (equals()).sorted - orderered elements (Comparator).limit - maximum number of elements.substream - range (by index) of elements

List<Person> persons = ...;Stream<Person> tenPersonsOver18 = persons.stream() .filter(p -> p.getAge() > 18) .limit(10);

Page 58: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamTerminating operationsTerminating operations

1. Obtain a stream from some sources2. Perform one or more intermidate operations3. Perform one terminal operation

reducers like reduce(), count(), findAny(), findFirst()collectors (collect())forEachiterators

List<Person> persons = ..;List<Student> students = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new) .collect(Collectors.toList());

Page 59: Automatic Migration of Legacy Java Method Implementations to Interfaces

java.util.streamjava.util.streamParallel & SequentialParallel & Sequential

List<Person> persons = ..;List<Student> students = persons.stream() .parallel() .filter(p -> p.getAge() > 18) .sequential() .map(Student::new) .collect(Collectors.toCollection(ArrayList::new));

Page 60: Automatic Migration of Legacy Java Method Implementations to Interfaces

References & Documentation &References & Documentation &Interesting LinksInteresting Links

Horstmann, Cay S. (2014-01-10). Java SE8 for the Really Impatient: A Short Course on theBasics (Java Series). Pearson Education.

http://download.java.net/jdk8/docs/http://download.java.net/jdk8/docs/api/https://blogs.oracle.com/thejavatutorials/entry/jdk_8_documentation_developer_previewhttp://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Page 61: Automatic Migration of Legacy Java Method Implementations to Interfaces

Questions?Questions?