How can I refer to a super method in a Java class that implements an interface but does not extend another class?

I have several Java classes that extend various implementations of the generic List interface. They just write down everything that is added to the list.

The following is a list of LoggingArrayList. As the name suggests, it extends ArrayList. The LoggingLinkedList class is identical, except that it extends LinkedList.

My main goal is not to duplicate all the common code so that I can use a different base class. I try to adhere to the principle of DRY (Do not Repeat Yourself) as much as possible.

First of all, please do not suggest a better way to log. This is not my real application at all. This is an easy way to demonstrate the problem I am facing.

I have two closely related questions. The first is the question in the title. How can I refer to the super method in a Java class that implements an interface but does not extend another class?

The LoggingArrayList class, as shown below, works great, but when I change the class declaration from ... extends ArrayList to ... implements List, the three super.method () references are no longer invoked, so my first question.

A good answer to my second question will almost make the first question moot. The second question is: is there a way to declare an abstract base class, or perhaps an interface that extends the list with the default implementation of the various add () methods, so that I can simply extend this abstract base class or implement this interface and specify only which list will be the basis for a particular class?

For example, I would like to do something like this:

interface LoggingList<T extends Object, L extends List<T>> extends L { // overloaded methods go here as shown below // with overloaded methods declared as default for the interface } 

... then I could just implement a LoggingList once for each concrete implementation of List without duplicating all the common code. Specific classes might look something like this without the extra code needed inside their braces:

 public class LoggingArrayList<T extends Object> implements LoggingList<T, ArrayList<T>> {} public class LoggingLinkedList<T extends Object> implements LoggingList<T, LinkedList<T>> {} 

The problem is that the interface definition, as I suggested, is invalid (will not compile), and the super.method (s) links in the code below are not available unless I make LoggingList an abstract subclass of the interface, and then I return back where I am now.

Thanks in advance for any ideas on how to fulfill my dry goal.

Here is my whole LoggingArrayList class.

 public abstract class LoggingArrayList<T extends Object> extends ArrayList<T> { protected void log(T e) { System.out.println(e == null ? "null" : e.toString()); } @Override public boolean add(T e) { log(e); // How do I reference a super.method() // in a class that implements an interface // but does not extend another class? return super.add(e); } @Override public boolean addAll(Collection<? extends T> clctn) { boolean anyChanges = false; for(T e : clctn) { // ensure that we call our overridden version of add() // so it gets logged. anyChanges = anyChanges || add(e); } return anyChanges; } @Override public boolean addAll(int i, Collection<? extends T> clctn) { for(T e : clctn) { // ensure that we call our overridden version of add() // so it gets logged. add(i, e); i++; // keep the inserted elements in their original order } return !clctn.isEmpty(); } @Override public T set(int i, T e) { log(e); // How do I reference a super.method() // in a class that implements an interface // but does not extend another class? return super.set(i, e); } @Override public void add(int i, T e) { log(e); // How do I reference a super.method() // in a class that implements an interface // but does not extend another class? super.add(i, e); } } 
+5
source share
3 answers

One way to do this: Delegate.

Instead of having multiple implementations for different types of lists, you can just have one

 public class LoggingList<T extends Object> implements List<T> protected List<T> superList; public LoggingList(List<T> anotherList) { superList= anotherList; } protected void log(T e) { System.out.println(e == null ? "null" : e.toString()); } @Override public boolean add(T e) { log(e); return superList.add(e); } 

And then instead of calling super. you call superList.

You still have to adapt your constructors from new LoggingLinkedList() to new LoggingList(new LinkedList()); - but it should not be a biggie ...

+3
source

You can use the JDK proxy for this purpose. Just google "how to use the jdk proxy class".

Your use case is described here.

+1
source

For further use, a complete working solution is proposed here, which I compiled using the proxy approach, as suggested by Sergey Morozov. The kernel is less than 50 lines of code. More than half of the code included at the end is just a unit test for actual functionality.

I'm still not sure if I will eventually use this approach for my purpose, but it was a very useful exercise. Thank you for offering Sergey.

 public class LoggingListProxyFactory { protected static void log(String methodName, Object element) { System.out.println(methodName + ": [" + (element == null ? "null" : element.toString()) + "]" ); } public static <T extends Object> List<T> getProxy(final List<T> list) { return (List<T>) Proxy.newProxyInstance( list.getClass().getClassLoader(), new Class[]{ List.class }, new LoggingInvocationHandler(list) ); } private static class LoggingInvocationHandler<T> implements InvocationHandler { final List<T> underlyingList; public LoggingInvocationHandler(List<T> list) { this.underlyingList = list; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // These are the List interface methods that we want to log. // In every case, the new elements happen to be the last parameter. // // boolean add(Object e) // void add(int index, Object element) // boolean addAll(Collection c) // boolean addAll(int index, Collection c) // Object set(int index, Object element) String methodName = method.getName(); if( ( "add".equals(methodName) | "addAll".equals(methodName) | "set".equals(methodName) ) // a few additional probably unnecessary checks && args != null && args.length == method.getParameterCount() && method.getParameterCount() > 0 ) { log(methodName, args[args.length-1]); } return method.invoke(underlyingList, args); } } public void testGetProxy() { List<String>[] testLists = new List[] { new ArrayList<>(), new LinkedList<>() }; for(List<String> aList : testLists) { List<String> proxy = LoggingListProxyFactory.getProxy(aList); // aList.add(42); // type is enforced at compile time aList.add(aList.getClass().getSimpleName()); aList.add("unlogged"); aList.add(null); // proxy.add(42); // type is enforced at compile time proxy.add(proxy.getClass().getSimpleName()); // exercise each the methods that are being logged proxy.add("foo"); proxy.add(0, "bar"); proxy.add(null); proxy.addAll(aList); proxy.addAll(7, aList); proxy.set(5, "five"); System.out.println(); System.out.println(aList.getClass().getSimpleName() + ".size() = " + aList.size()); aList.stream().forEach(System.out::println); System.out.println(); System.out.println("proxy.size() = " + proxy.size()); proxy.stream().forEach(System.out::println); } } } 
+1
source

Source: https://habr.com/ru/post/1237388/


All Articles