The skeletal implementation pattern is a software design pattern consisting of defining an abstract class that provides a partial interface implementation. However, since Java allows only single class inheritance, if implementers decide to extend a skeletal implementation, they will not be allowed to extend any other class. Also, discovering the skeletal implementation may require a global analysis.
Java 8 enhanced interfaces alleviate these problems by allowing interfaces to contain (default) method implementations, which implementers inherit. Java classes are then free to extend a different class, and a separate abstract class is no longer needed; developers considering implementing an interface need only examine the interface itself.
In this talk, I will argue that both these benefits improve software modularity, and I will discuss our ongoing work in developing an automated refactoring tool that would assist developers in taking advantage of the enhanced interface feature for their legacy Java software.
Raffi Khatchadourian is an Assistant Professor in the Department of Computer Systems Technology (CST) at New York City College of Technology (NYCCT) of the City University of New York (CUNY) and an Open Educational Resources (OER) Fellow for the Spring 2016 semester. His research is centered on techniques for automated software evolution, particularly those related to automated refactoring and source code recommendation systems. His goal is to ease the burden associated with correctly and efficiently evolving large and complex software by providing automated tools that can be easily used by developers.
Raffi received his MS and PhD degrees in Computer Science from Ohio State University and his BS degree in Computer Science from Monmouth University in New Jersey. Prior to joining City Tech, he was a Software Engineer at Apple, Inc. in Cupertino, California, where he worked on Digital Rights Management (DRM) for iTunes, iBooks, and the App store. He also developed distributed software that tested various features of iPhones, iPads, and iPods.
2. Some History
Java was invented in the 90's as an Object-Oriented language.
Largest change was in ~2005 with Java 5.
Generics.
Enhanced forloops.
Annotations.
Type-safe enumerations.
Concurrency enhancements (AtomicInteger).
4. Lambda Expressions
A block of code that you can pass around so it can be
executed later, once or multiple times.
Anonymous methods.
Reduce verbosity caused by anonymous classes.
7. Using Lambdas In Collections
Java 8 introduced a forEach()method for Collections that accepts a
lambda expression.
List<String>myList=Arrays.asList({"one","two","three"});
myList.forEach((Strings)->{System.out.println(s););
//printseachstringinthelist.
Or, more simply:
myList.forEach(s->System.out.println(s));
//thetypeofsisinferred.
12. Default Methods
Traditionally, interfaces can't have method definitions (just
declarations).
Default methods supply default implementations of interface
methods.
By default, implementers will receive this implementation if they don't
provide their own.
19. Of course not (Java is not C++)!
class Clazzinherits defaults for foo() from both
types Aand B
How do we fix it?
20. Option A:
publicclassClazzimplementsA,B{
publicvoidfoo(){/*...*/}
}
We resolve it manually by overriding the conflicting method.
Option B:
publicclassClazzimplementsA,B{
publicvoidfoo(){
A.super.foo();//orB.super.foo()
}
}
We call the default implementation of method foo()from either
interface Aor Binstead of implementing our own.
21. Going back to the example of forEachmethod, how can we force it's
default implementation on all of the iterable collections?
22. 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 default
forEachmethod was added to the Iterable<E>interface.
23. We can find its default implementation in java.lang.Iterable
interface:
@FunctionalInterface
publicinterfaceIterable{
Iteratoriterator();
defaultvoidforEach(Consumer<?superT>action){
Objects.requireNonNull(action);
for(Tt:this){
action.accept(t);
}
}
}
24. The forEachmethod takes a java.util.function.Consumer
functional 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 classes
implement the Iterableinterface.
Specific collections, e.g., UnmodifiableCollection, may override
the default implementation.
25. Summary
Default methods can be seen as a bridge between lambdas and JDK
libraries.
Can be used in interfaces to provide default implementations of
otherwise abstract methods.
Clients can optionally implement (override) them.
Can eliminate the need for skeletal implementators like
.
Can eliminate the need for utility classes like .
staticmethods are now also allowed in interfaces.
AbstractCollection
Collections
27. Motivation
Two 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 skeletal
implementation required.
2. Partial migration of skeletal implementation to interface.
Possibly makes interfaces easier to implement.
All implementations may not need to extend the skeletal
implementation.
Possibly makes interfaces easier to maintain.
Possibily less simultaneous modifications between interface and
skeletal implementation required.
28. Open Problems
Can eliminate the need for skeletal implementators like
.AbstractCollection
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?
30. Why Is This Complicated?
Corresponds to moving a skeletal implementation to an interface.
# From a class To an interface
1. instancemethod defaultmethod
Can we not simply use a move method refactoring?
No. The inherent problem is that, unlike classes,
interfaces may extend multiple interfaces. As such, we
now have a kind of multiple inheritance problem to
deal with.
31. Some (perhaps) Obvious
Preconditions
Source instancemethod must not access any instance fields.
Interfaces may not have instance fields.
Can't currently have a defaultmethod in the target interface with
the same signature.
Can't modify target method's visibility.
32. Some Obvious Tasks
Must replace the target method in the interface(add a body to it).
Must add the defaultkeyword.
Must remove any @Overrideannotations (it's top-level now).
If nothing left in abstractclass, remove it?
Need to deal with documentation.
33. Some Not-so-obvious
Preconditions
Suppose we have the following situation:
interfaceI{
voidm();
}
interfaceJ{
voidm();
}
abstractclassAimplementsI,J{
@Override
voidm(){...}
}
Here, Aprovides a partial implementation of I.m(), which also happens
to be declared as part of interface J.
34. Some Not-so-obvious
Preconditions
Now, we pull up A.m()to be a default method of I:
interfaceI{
defaultvoidm(){..}//wasvoidm();
}
interfaceJ{
voidm();//staysthesame.
}
abstractclassAimplementsI,J{
//nowempty,was:voidm(){...}
}
We now have a compile-time error in Abecause Aneeds to declare
which m()it inherits.
In general, inheritance hierarchies may be large and complicated.
Other preconditions may also exist (work in progress).
36. References & Documentation &
Interesting Links
Horstmann, Cay S. (2014-01-10). Java SE8 for the Really Impatient: A Short Co
Basics (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_develo
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html