Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can not populate default methods, when interfaces are in classpath and not in input directory #98

Open
teras opened this issue Jun 3, 2016 · 4 comments

Comments

@teras
Copy link

teras commented Jun 3, 2016

Let's have an interface in a stand alone project. The compiled classes can be found (let's say) under target/lib . The source is as follows:

package test;

public interface AnInterface {

    public void one();

    public default void two() {
    }
}

Then, let's assume that we have another class, where the compiled classes could be found under target/classes, and the code is as follows:

package test;

public class Main implements AnInterface {

    public static void main(String[] args) {
        Main main = new Main();
        main.one();
        main.two();
    }

    @Override
    public void one() {
    }
}

If we execute retrolambda like this, java -Dretrolambda.inputDir=target/classes -Dretrolambda.classpath=target/classes:target/lib -Dretrolambda.defaultMethods=true -jar retrolambda-2.3.0.jar then javap produces the next output:

Compiled from "Main.java"
public class test.Main implements test.AnInterface {
  public test.Main();
  public static void main(java.lang.String[]);
  public void one();
}

If I move the interface to the inputdir folder, then I get this output:

Compiled from "Main.java"
public class test.Main implements test.AnInterface {
  public test.Main();
  public static void main(java.lang.String[]);
  public void one();
  public void two();
}

I would expect that, no matter where the interface is, the "produced" Main class to have the missing method injected, as well as the produced test/AnInterface$.class to appear in the output folder. But this is not the case.

Am I missing something?
Is this a feature request?

@luontola
Copy link
Owner

luontola commented Jun 3, 2016

This is one of the limitations of backporting default methods. It requires the interface and all classes that implement it to be together in the same inputdir. https://github.com/orfjackal/retrolambda#known-limitations

@luontola
Copy link
Owner

luontola commented Jun 3, 2016

Ideas of how to implement the backporting reliably without that limitation are welcome. Pull requests even more so.

One option might be to tag all backported interfaces and if Retrolambda finds that tag in an implemented interface, it would add the necessary method implementations. Or it could check if an AnInterface$ class exists and it has matching methods (maybe annotated with some Retrolambda specific metadata).

@teras
Copy link
Author

teras commented Jun 4, 2016

On the restrictions this wasn't clear. It is said only "in one go", which makes sense and in this exemple is "in one go".

I'll be happy to help coding, although not proficient with Retrolambda.

Ideas now. I was thinking something like

  • When transforming, keeping track (in a Set?) of which interfaces are half-implemented. The code should be already there, because if I try the transformation without option -Dretrolambda.defaultMethods=true, then a warning is displayed.
  • After all has finished, gather the required interfaces from the classpath (not from input directory) and produce only the Interface$.class structure, following the already existing method of retrolambda.

An alternate pipeline, depending on Retrolambda own pipeline could be:

  • Pre-process all classes and see if an interface is half implemented (like before)
  • Copy the interfaces from the classpath to the input directory, so that we can process as usual
  • Delete the Interface.class files from output directory, and leave Interface$.class only

I am not general in favour of keeping the Interface.class files, since these will appear in the original classpath anyway, and for someone to use it, there is a need to transform the original library to Java 7 anyway. Thus, deleting will leave a cleaner code IMHO.

PS: I am thinking to do the second option in my own project anyway, since I can't right now update retrolambda.

@teras
Copy link
Author

teras commented Jul 6, 2016

Hello again. Any comments on the pull request I proposed to solve this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants