Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to adapt to changes in API

I have a large project that depends on certain library. One of this library classes exposed the following methods:

public void until(final Predicate<T> isTrue)
public <V> V until(Function<? super T, V> isTrue) {

where Predicate and Function are:

com.google.common.base.Predicate<T>
com.google.common.base.Function<? super T, V>

In the last version of that library the above has been changed and was refactored by deprecating(removing) the public void until(final Predicate<T> isTrue) method and amending the second method as follows:

public <V> V until(Function<? super T, V> isTrue)

where Function is now java.util.function.Function<? super T, V>.

As far as I understand the above changes has been done to fully support Java 8 features and specifically lambda expressions.

Now to my question. While I can transform all calls to until that used the deprecated version of this method to the new structure, I am afraid there are too many places where this method is called. Is there a way I can keep using the old (deprecated) method by maybe re-declaring it elsewhere myself or something of a kind? Any alternative ways to solve this problem are happily accepted.

like image 540
Eugene S Avatar asked Oct 30 '22 06:10

Eugene S


1 Answers

IF you don't want to change your source code, one possible solution is copy the source code from the library which class have until(Function) method in your source code. and then adding a removed method until(Predicate) in your source code. the final code like as below:

package ???;//the package of the library which class have `until` method

class ??? {//the class copied from the library which have `until` method
     <T> boolean until(Predicate<T> predicate){
         return until(new Function<T,Boolean>(){
             public Boolean apply(T value){
                return predicate.apply(value);
             }
         });
     }

     <T,R> R until(Function<T,R> function){
        // the source code from the library
     }
     //...  other source code from the library
}

the ClassLoader will load your class copied from the library only rather than from the library. since the ClassLoader uses a delegation model to search the classes / resources and an AppClassLoader will always find a class in classpath and then external jars in turn:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.

Another option is write your own ClassLoader, and then write a proxy to access your library class. this useful when you can't copy the external library class since it depends many its internal classes. for example, let's say you have a library class as below:

public class Library<T> {
    private final List<T> items;

    public Library(T... items) {
        this.items = Arrays.asList(items);
    }

    public boolean exists(Function<? super T, Boolean> condition) {
        return items.stream().anyMatch(condition::apply);
    }
}

Then declaring an interface to matching the library class as below:

public interface LibraryAccess<T> {
    boolean exists(Function<? super T, Boolean> condition);
}

Last write your own class overwrite the library class as below:

public class Library<T> {

    private final LibraryAccess<T> library;

    public Library(T... items) {
        // create library from external jars by your own ClassLoader & reflect api
        library = LibraryAccess.newInstance(items);
    }

    public boolean exists(Predicate<? super T> condition) {
        return library.exists(condition::test);
    }

    public boolean exists(Function<? super T, Boolean> condition) {
        return library.exists(condition);
    }
}

the whole source & test code I have checked in github.

like image 100
holi-java Avatar answered Nov 02 '22 23:11

holi-java