0

Consider the following example:

public interface Greeter {
    String greet();
}

public class ExplicitGreeterImpl implements Greeter {
    @Override
    public String greet() {
        return "Hello World!";
    }
}

public class ImplicitGreeterImpl {
    public String doTheGreeting() {
        return "Hello World!";
    }
}

private void run() {
    System.out.println(new ExplicitGreeterImpl().greet());

    Greeter foo = new ImplicitGreeterImpl()::doTheGreeting;
    System.out.println(foo.greet());
}

The functional interface Greeter has two implementations. ExplicitGreeterImpl implements Greeter using the implements clause, while ImplicitGreeterImpl::doTheGreeting implements Greeter without it. Nevertheless, ImplicitGreeterImpl::doTheGreeting is designed to implement Greeter, just like ExplicitGreeterImpl.

Now, I want to refactor the Greeter interface, so I can pass a name to it:

public interface Greeter {
    String greet(String name);
}

I can do this with the Change Method Signature refactoring provided by Eclipse (I am sure other IDEs have a similar refactoring). This automatically updates all implementations and usages of the Greeter interface. Implementations receive the new parameter, while usages pass a configurable default value. This works fine for the ExplicitGreeterImpl, however the refactoring does not touch the ImplicitGreeterImpl::doTheGreeting method. Thus, the assignment

Greeter foo = new ImplicitGreeterImpl()::doTheGreeting;

becomes a compile-time error. To fix this, I have to manually adjust the signature of the method ImplicitGreeterImpl::doTheGreeting.

Now, I understand that it is undesirable in many cases to automatically adjust the signature of ImplicitGreeterImpl::doTheGreeting. However, I feel that the current workflow can be improved:

  • Eclipse does not display a warning in the refactoring preview that suggests that there will be a compile-time error.
  • It should be possible to annotate the method to clarify that it is supposed to implement a given functional interface.

For example, ImplicitGreeterImpl could look like this:

public class ImplicitGreeterImpl {
    @Implements(Greeter.class)
    public String doTheGreeting() {
        return "Hello World!";
    }
}

Now, refactoring tools could be sure that ImplicitGreeterImpl::doTheGreeting is supposed to implement Greeter and thus, they can automatically change its signature.

Thus, my question is: Is there a way to tell refactoring tools that a given method is supposed to implement a given functional interface? I searched for the annotation proposed above, but I did not find anything useful.

Stefan Dollase
  • 4,530
  • 3
  • 27
  • 51

2 Answers2

0

Yes: you need to implement the interface. If you can add an annotation linking to the interface, why not make your class implement it?

JnRouvignac
  • 807
  • 5
  • 19
  • There is actually a good reason to not implement the interface directly, but to use the method reference. Consider the command pattern. In this case: (1) The Command interface is probably a functional interface, (2) there are many implementations of the Command interface and (3) each of these implementations is very simple. Adding an extra class for each of these implementations adds a lot of noise to the code. However, if each Command implementation is just a method, the code becomes much cleaner. – Stefan Dollase Sep 25 '16 at 20:52
0

I kept searching for an answer, but there does not seem to be a solution that uses an annotation. However, when asking the question I had a specific use case in mind: I want to write down many different implementations of the same functional interface in a single file. Indeed, there is a solution for this problem, which also works great with automatic refactoring tools:

public interface Greeter {
    String greet();
}

public enum EnumGreeterImpl implements Greeter {
    GREETER1 {
        @Override
        public String greet() {
            return "Hello World!";
        }
    },
    GREETER2 {
        @Override
        public String greet() {
            return "Foo bar";
        }
    },
}

private void run() {
    Greeter foo = EnumGreeterImpl.GREETER1;
    System.out.println(foo.greet());
}

See also:

Community
  • 1
  • 1
Stefan Dollase
  • 4,530
  • 3
  • 27
  • 51