10

I'm not sure if this is possible or not, but what I want to accomplish is this:

public static <A,B extends SomeClass & A> B makeB(A thing) {...}

Essentially, using a reflection/generation driven process, I want to provide a thing of type B, where B is of class SomeClass and implements interface A, and A is user-supplied through Generics.

I am not asking about the mechanics of generating B - I have that under control. What I'm looking for is a way to restrict generic type argument <A> to interfaces, not classes, so that I can use the syntax B extends SomeClass & A for clean type safety.

Is this possible? Is anyone aware of an alternative approach to this problem?


Edit: I guess I didn't express myself very clearly, as it seems to be causing confusion in the comments:

B is intended to be a placeholder for a wildcard, so that the client can get a single object that is both a SomeClass and an A without having to do casting based on trust. The client will not have access to the name of the actual class that implements SomeClass and A, because it's being generated at compile time, hence this issue regarding type safety.

8
  • 6
    No, it isn't possible. Commented Jul 12, 2013 at 19:45
  • 1
    This gives me a sad face :( Commented Jul 12, 2013 at 19:46
  • 3
    On the other hand, if you want a reflection-driven thing, you could just pass a Class<A> and explicitly call Class.isInterface(). Commented Jul 12, 2013 at 19:47
  • @LouisWasserman He already has an instance of A as a parameter. He can get Class<A> from that. He doesn't need another parameter. Commented Jul 13, 2013 at 1:39
  • 1
    @jahroy Sorry, I didn't get a chance to see your comment to me until after you deleted your answer. Your point about inferring B is absolutely right - I actually mentioned a similar concern in the edit to my answer. You should definitely undelete your answer because it's helpful. Commented Jul 17, 2013 at 1:56

3 Answers 3

5

It's impossible to impose such a compile-time restriction. Generic type parameters are stand-ins for reference types; they make no distinction between class types and interface types. The fact that additional bounds in a type parameter's declaration must be interface types is merely incidental - your strategy to leverage this as a means to impute a type as an interface was clever, but it's defeated by the limitation that type parameters can't be used in multiple bounds.

Your only options are to settle for a runtime check using Class.isInterface() like Louis Wasserman pointed out, or to leave it up to the caller to be responsible with what it passes in. Either way, make sure to clearly document the method's expectations and behavior.


B is intended to be a placeholder for a wildcard, so that the client can get a single object that is both a SomeClass and an A without having to do casting based on trust. The client will not have access to the name of the actual class that implements SomeClass and A

This seems like a contradiction to me. There's no point to declaring B if the caller can't possibly know what it evaluates to. Remember: the caller of a generic method provides its type arguments. So a caller deciding B without anything to base it on can only be guessing - and that can never be type-safe.

It seems like what you really want your method to return is some type that is both a SomeClass and an A, but this is tricky because they don't share a common supertype:

public static <A> SomeClass&A makeSomeClass(A thing) {...}

(this is nonsensical syntax for demonstration purposes only)

As a workaround, consider alternative ways to represent both a SomeClass and some interface type. For example the candidate interfaces could have a common method for returning a SomeClass:

public interface IsSomeClass {
    SomeClass asSomeClass();
}

public interface Foo extends IsSomeClass { }

The implementation of asSomeClass would in fact just return this. Then you could do:

public static <A extends IsSomeClass> A makeSomeClass(Class<A> type) {...}

And the caller of that method would be able to use the returned object as either type:

final Foo foo = makeSomeClass(Foo.class);
final SomeClass someClass = foo.asSomeClass();

If the interfaces themselves can't be modified, then another option is to use a wrapper class and composition instead:

final class SomeClassWrapper<A> {

    private final SomeClass someClass;
    private final A a;

    //constructor and getters, etc.
}

And your method would return a wrapper instance instead, assigning the implementation instance to both someClass and a:

public static <A> SomeClassWrapper<A> makeSomeClass(Class<A> type) {...}
Sign up to request clarification or add additional context in comments.

Comments

1

If SomeClass is always a class, the A in <B extends SomeClass & A> can only be an interface, because there is no multiple inheritance in Java. The only way & A can be satisfied is if A is an interface.

5 Comments

you might be overlooked, but the compiler complains: The type A is not an interface; it cannot be specified as a bounded parameter
@jahroy and @EJP you seem to be overlooking that A is a type parameter in <A,B extends SomeClass & A>, which is why that doesn't compile.
yeah...you are right...but there is no way to tell the compiler that the generic argument will be an interface?
@ZoltánNagy That's the point. If the actual type argument supplied as A isn't an interface the compiler complains.
@ZoltánNagy - The point of generics is to get the compiler to help you enforce type safety. If you want A to be an interface and the compiler complains when it is not, then generics are doing their job.
1

I think the problem here is that you want to return a B from this method.

You specify B as a type parameter, but it never appears anywhere else in the method signature.

How is the compiler supposed to infer the return type from the arguments????

There is no opportunity for the client code to specify what B is.

It seems like you should return either a SomeClass or an A.

Either one can be a B under the hood, but should appear as a SomeClass or an A to the client code.

Comments

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.