5

Consider the following scenario: Say that you created an interface Foo:

public interface Foo {

    public void bar();

}

And say that there is an old class SomeOldClass in a certain library that you want to use. It already has the bar() method, but does not explicitly implement Foo.

You have written the following code for all classed that implement Foo:

public <T extends Foo> T callBarOnThird(List<T> fooList){
    return fooList.get(2).bar();
}

And now you want it to also work for SomeOldClass. You dont have access to the source code of this class, so you can't modify it.

Is there a way to declare Foo or something similar as some sort of "soft" interface, (as in where any class that implements all the required methods would be accepted as an implicit implementation of the soft interface)? If not, how would you solve this with code that is as clean as possible?

5
  • add 'implements Foo' to the class. if it doesn't implement it, it's not an instanceof the interface Commented Sep 27, 2018 at 7:34
  • "an implicit implementation" - this is not a thing Commented Sep 27, 2018 at 7:35
  • You can use the decorator to handle this. Commented Sep 27, 2018 at 7:45
  • do you know any languages that might support this btw? Commented Sep 27, 2018 at 11:00
  • @Eugene I dont know for sure, but I think Go has soft interfaces Commented Sep 27, 2018 at 19:00

3 Answers 3

5

No, it does not.

You have to provide an adapter instance (there are several methods and tools to help with that, but Java does not do it "implicitly").

Sign up to request clarification or add additional context in comments.

Comments

4

Java is statically typed and dynamically bind.

Dynamically bind: This means that the linking between a method signature and its implementation happens at runtime. For example.

For example

public interface MyInterface {

    void doStuff();

}

public class MyFirstImpl implements MyInterface {

   @Override
   public void doStuff() {
       // do some stuff here
   }

}

public class MySecondImpl implements MyInterface {

   @Override
   public void doStuff() {
       // do some stuff here
   }

}

So if you would have the next snippet

MyInterface test; // pointing to either MyFirstImpl or MySecondImpl 
test.doStuff();

The JVM will determine at runtime weather to call the doStuff method from MyFirstImpl or MySecondImpl based on the runtime type of the object.

Statically typed: This means that the JVM will check at compile time weather a there is a method to call regardless of the implementation.

For example:

public interface MyInterface {

    void doStuff();

}

public class MyFirstImpl implements MyInterface {

   // no override here
   public void doStuff() {
       // do some stuff here
   }

}

public class MySecondImpl implements MyInterface {

   // no override here
   public void doStuff() {
       // do some stuff here
   }

}

So if you would have the next snippet

MyInterface test; // pointing to either MyFirstImpl or MySecondImpl 
test.doStuff();

The compiler will complain because it can't ensure at compile time that regardless of the implementation of MyInterface there is a doStuff method to call (although in this case, both implementations of MyInterface define a doStuff method).

This ensures that you won't get a NoSuchMethodException at runtime, if you would pass, for example, the next implementation.

public class MySecondImpl implements MyInterface {

   // no override here
   // no doStuff method

}

This adds some type safety to the language at the cost of some rigidity (since you are able to determine the issue earlier than at runtime and therefore you have a shorter feedback loop, at the cost of the scenario in which all the implementations actually expose the method not working out of the box).

How you should refactor your code:

Create a wrapper over the third party library and expose the interface from the wrapper.

public interface Foo {

    void bar();

}

public class ThirdPartyFooWrapper implements Foo {

     private SomeOldClass oldClass;

     public ThordPartyFooWrapper (SomeOldClass oldClass){
          this.oldClass = oldClass;
     }

     @Override
     public void bar() {
         this.oldClass.bar();
     }

}

Then, in your code use ThirdPartyFooWrapper instead of SomeOldClass.

Hope this answers your question!

3 Comments

I should have known, considering almost everything in Java is explicit. Thanks for your answer. It was really helpful. Are there any libraries that add annotations to automatically create a wrapper for these situations?
Glad I could help you. I am not aware of any such libraries, but if you don't have a lot of SomeOldClass, writing your own wrappers shouldn't be a big overhead.
It would be nice if there was a compile-time class annotation for ThirdPartyWrapper named something like @Autobox, with the field annotation for oldClass being @AutoboxTarget, so that you didn't need to manually implement the wrapper and that any SomeClass object could be used as if it were the wrapper object.
0

Extension to Thilos answer.

You can also use the decorator to handle this

public <T extends Foo> T callBarOnThird(List<T> fooList){
    return new BarDecorator(fooList.get(2)).bar();
}

Inside the decorator, you can check if given Object is the instance of Foo or not then do operations accordingly.

4 Comments

Conceptually, the pattern to use is the adapter. decorators are for different use cases (chain method call to several objects implementing the same interface, with some enrichment)
The intent of Decorator is sourcemaking.com/design_patterns/decorator I did not get your point. Can you please elaborate?
Though I got the point that Adaptor is right use case for this sourcemaking.com/design_patterns/adapter
the decorator is: all objects implements the same interface, they are constructed with a reference given in the constructor to another instance. each method call then delegates to the "wrapped" instance + adds some behavior (think of Streams). Adapter is a way to see an object as a different interface he doesn't implement. They are never required to all implement the same interface (that's the point of the pattern: make a square piece adapt to a circle hole). Ps: btw, your code sample does not really illustrate a decorator

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.