Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics restrict to interface

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.

like image 692
torquestomp Avatar asked Jul 12 '13 19:07

torquestomp


People also ask

What are the restrictions of generics in Java?

To use Java generics effectively, you must consider the following restrictions: Cannot Instantiate Generic Types with Primitive Types. Cannot Create Instances of Type Parameters. Cannot Declare Static Fields Whose Types are Type Parameters. Cannot Use Casts or instanceof With Parameterized Types.

How do you implement a generic interface in Java?

Another way is to create a generic class that implements the generic interface. The class definition uses the same formal type parameters twice – one after the class name and another after the interface name it implements. Consider the following codes. They use <U> twice in the two locations. 3.

What is the difference between generic and non generic classes in Java?

A non-generic class can be used if a specific parameterized type is provided with the generic interface. A Java generic interface is very much like any other interface. It can be used to declare a variable and can be returned from a method. It can also be passed as an argument.

Can a generic type parameter be static in Java?

The generic type parameter cannot be static. GenericsExample.java:3: error: non-static type variable T cannot be referenced from a static context static T age; ^ 1 error You cannot cast parameterized type of one datatype to other.


1 Answers

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) {...}
like image 181
Paul Bellora Avatar answered Oct 03 '22 04:10

Paul Bellora