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

Default methods aren't skipped #1104

Open
nathanmerrill opened this issue May 22, 2017 · 5 comments
Open

Default methods aren't skipped #1104

nathanmerrill opened this issue May 22, 2017 · 5 comments

Comments

@nathanmerrill
Copy link

nathanmerrill commented May 22, 2017

Interfaces can have default methods since Java 8. There was an attempted code fix to ignore them in the FactoryProvider: 85f14e0

However, the fix doesn't work because the condition needs to be || instead of &&. (Default methods aren't synthetic or bridge)

This makes it so that if you call any default method in an interface, you end up with the Guice-provided method instead of the default one.

public interface FooFactory {
    Foo create();

    default Foo createWithData(Data data){ 
        Foo foo = create();  // This will never be called, even if you call `fooFactory.createWithData(data)`
        foo.addData(data);
        return foo;
    }
}
@dimo414
Copy link
Contributor

dimo414 commented May 22, 2017

Thanks for the bug report Nathan, any chance you'd be willing to send a pull request (along with a test case)?

@nathanmerrill
Copy link
Author

nathanmerrill commented May 23, 2017

I'm not confident that I know what the fix is. I think that the fix is to use an ||, but with the commit, they added two lines:

validateFactoryReturnType(errors, method.getReturnType(), factoryRawType);
defaultMethods.put(method.getName(), method);

which I'm not sure what it does, and whether it applies to all three types.

As far as testing, where would that test go? I imagine somewhere in this package, would I simply add another class to that package?

@sameb
Copy link
Member

sameb commented May 23, 2017

Note that 85f14e0 wasn't intended to fix "default" methods per say -- it was intended to fix synthetic/bridge methods that java8 created as default methods. See the issue (#904) for more details.

That said, skipping non-bridge/synthetic default methods may be desirable, but also may be backwards incompatible. We reviewed a similar change internally (for reference: cl/148494483), but the review stalled because we couldn't come up with a good answer for how to make it work without silently changing behavior for folks that might have relied on this.

FWIW, the validateFactoryReturnType line is about ensuring the return type visibility matches the factory type visibility -- otherwise the java.lang.reflect.Proxy freaks out. The message in that method gives a good explanation.

The defaultMethods map is used to keep track of our default methods so that our Proxy implementation knows how to call it. Making this work is made more complex by the fact that the JDK doesn't give any good way to call a default method, so we have to hack around it. The CL description gives some good explanation of what we're doing here.

@nathanmerrill
Copy link
Author

nathanmerrill commented May 23, 2017

You mention skipping non-bridge/synthetic default methods may be backwards incompatible: I'm struggling to come up with a use-case of somebody writing a default method, but wanting it to be overridden by Guice.

That said, assuming that there is a use-case, I think it would it be possible to use some annotation to indicate to "skip this method"? I'm not a huge fan of introducing an entirely new annotation for a small edge case as this, but I think it would work.

@badbob
Copy link

badbob commented Feb 27, 2020

Is there are any progress on this issue?

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

No branches or pull requests

4 participants