How to reference a generic return type with multiple bounds
Solution 1
While the type parameters of a generic method can be restricted by bounds, such as extends Foo & Bar
, they are ultimately decided by the caller. When you call getFooBar()
, the call site already knows what T
is being resolved to. Often, these type parameters will be inferred by the compiler, which is why you don't usually need to specify them, like this:
FooBar.<FooAndBar>getFooBar();
But even when T
is inferred to be FooAndBar
, that's really whats happening behind the scenes.
So, to answer your question, such a syntax like this:
Foo&Bar bothFooAndBar = FooBar.getFooBar();
Would never be useful in practice. The reason is that the caller must already know what T
is. Either T
is some concrete type:
FooAndBar bothFooAndBar = FooBar.<FooAndBar>getFooBar(); // T is FooAndBar
Or, T
is an unresolved type parameter, and we're in its scope:
<U extends Foo & Bar> void someGenericMethod() {
U bothFooAndBar = FooBar.<U>getFooBar(); // T is U
}
Another example of that:
class SomeGenericClass<V extends Foo & Bar> {
void someMethod() {
V bothFooAndBar = FooBar.<V>getFooBar(); // T is V
}
}
Technically, that wraps up the answer. But I'd also like to point out that your example method getFooBar
is inherently unsafe. Remember that the caller decides what T
gets to be, not the method. Since getFooBar
doesn't take any parameters related to T
, and because of type erasure, its only options would be to return null
or to "lie" by making an unchecked cast, risking heap pollution. A typical workaround would be for getFooBar
to take a Class<T>
argument, or else a FooFactory<T>
for example.
Update
It turns out I was wrong when I asserted that the caller of getFooBar
must always know what T
is. As @MiserableVariable points out, there are some situations where the type argument of a generic method is inferred to be a wildcard capture, rather than a concrete type or type variable. See his answer for a great example of a getFooBar
implementation that uses a proxy to drive home his point that T
is unknown.
As we discussed in the comments, an example using getFooBar
created confusion because it takes no arguments to infer T
from. Certain compilers throw an error on a contextless call to getFooBar()
while others are fine with it. I thought that the inconsistent compile errors - along with the fact that calling FooBar.<?>getFooBar()
is illegal - validated my point, but these turned out to be red herrings.
Based on @MiserableVariable's answer, I put together an new example that uses a generic method with an argument, to remove the confusion. Assume we have interfaces Foo
and Bar
and an implementation FooBarImpl
:
interface Foo { }
interface Bar { }
static class FooBarImpl implements Foo, Bar { }
We also have a simple container class that wraps an instance of some type implementing Foo
and Bar
. It declares a silly static method unwrap
that takes a FooBarContainer
and returns its referent:
static class FooBarContainer<T extends Foo & Bar> {
private final T fooBar;
public FooBarContainer(T fooBar) {
this.fooBar = fooBar;
}
public T get() {
return fooBar;
}
static <T extends Foo & Bar> T unwrap(FooBarContainer<T> fooBarContainer) {
return fooBarContainer.get();
}
}
Now let's say we have a wildcard parameterized type of FooBarContainer
:
FooBarContainer<?> unknownFooBarContainer = ...;
We're allowed to pass unknownFooBarContainer
into unwrap
. This shows my earlier assertion was wrong, because the call site doesn't know what T
is - only that it is some type within the bounds extends Foo & Bar
.
FooBarContainer.unwrap(unknownFooBarContainer); // T is a wildcard capture, ?
As I noted, calling unwrap
with a wildcard is illegal:
FooBarContainer.<?>unwrap(unknownFooBarContainer); // compiler error
I can only guess that this is because wildcard captures can never match each other - the ?
argument provided at the call site is ambiguous, with no way of saying that it should specifically match the wildcard in the type of unknownFooBarContainer
.
So, here's the use case for the syntax the OP is asking about. Calling unwrap
on unknownFooBarContainer
returns a reference of type ? extends Foo & Bar
. We can assign that reference to Foo
or Bar
, but not both:
Foo foo = FooBarContainer.unwrap(unknownFooBarContainer);
Bar bar = FooBarContainer.unwrap(unknownFooBarContainer);
If for some reason unwrap
were expensive and we only wanted to call it once, we would be forced to cast:
Foo foo = FooBarContainer.unwrap(unknownFooBarContainer);
Bar bar = (Bar)foo;
So this is where the hypothetical syntax would come in handy:
Foo&Bar fooBar = FooBarContainer.unwrap(unknownFooBarContainer);
This is just one fairly obscure use case. There would be pretty far-ranging implications for allowing such a syntax, both good and bad. It would open up room for abuse where it wasn't needed, and it's completely understandable why the language designers didn't implement such a thing. But I still think it's interesting to think about.
Note - Since JDK 10 there is the var
reserved type name, which makes this possible:
var fooBar = FooBarContainer.unwrap(unknownFooBarContainer);
The variable fooBar
is inferred to have a type that implements both Foo
and Bar
and that cannot be denoted explicitly in source code.
A note about heap pollution
(Mostly for @MiserableVariable) Here's a walkthrough of how an unsafe method like getFooBar
causes heap pollution, and its implications. Given the following interface and implementations:
interface Foo { }
static class Foo1 implements Foo {
public void foo1Method() { }
}
static class Foo2 implements Foo { }
Let's implement an unsafe method getFoo
, similar to getFooBar
but simplified for this example:
@SuppressWarnings("unchecked")
static <T extends Foo> T getFoo() {
//unchecked cast - ClassCastException is not thrown here if T is wrong
return (T)new Foo2();
}
public static void main(String[] args) {
Foo1 foo1 = getFoo(); //ClassCastException is thrown here
}
Here, when the new Foo2
is cast to T
, it is "unchecked", meaning because of type erasure the runtime doesn't know it should fail, even though it should in this case since T
was Foo1
. Instead, the heap is "polluted", meaning references are pointing to objects they shouldn't have been allowed to.
The failure happens after the method returns, when the Foo2
instance tries to get assigned to the foo1
reference, which has the reifiable type Foo1
.
You're probably thinking, "Okay so it blew up at the call site instead of the method, big deal." But it can easily get more complicated when more generics are involved. For example:
static <T extends Foo> List<T> getFooList(int size) {
List<T> fooList = new ArrayList<T>(size);
for (int i = 0; i < size; i++) {
T foo = getFoo();
fooList.add(foo);
}
return fooList;
}
public static void main(String[] args) {
List<Foo1> foo1List = getFooList(5);
// a bunch of things happen
//sometime later maybe, depending on state
foo1List.get(0).foo1Method(); //ClassCastException is thrown here
}
Now it doesn't blow up at the call site. It blows up sometime later when the contents of foo1List
get used. This is how heap pollution gets harder to debug, because the exception stacktrace doesn't point you to the actual problem.
It gets even more complicated when the caller is in generic scope itself. Imagine instead of getting a List<Foo1>
we're getting a List<T>
, putting it in a Map<K, List<T>>
and returning it to yet another method. You get the idea I hope.
Solution 2
There are cases where a called method returning a value can be used by the caller without knowing the concrete type. It is even likely that such a type does not exist at all, it is only a proxy:
import java.lang.reflect.*;
interface Foo {}
interface Bar {}
class FooBar1 implements Foo, Bar {public String toString() { return "FooBar1"; }}
class FooBar2 implements Foo, Bar {public String toString() { return "FooBar2"; }}
class FooBar {
static <T extends Foo & Bar> T getFooBar1() { return (T) new FooBar1(); }
static <T extends Foo & Bar> T getFooBar2() { return (T) new FooBar2(); }
static <T extends Foo & Bar> T getFooBar() {
return (T)
Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class[] { Foo.class, Bar.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
return "PROXY!!!";}});
}
static <U extends Foo & Bar> void show(U u) { System.out.println(u); }
public static void main(String[] args) {
show(getFooBar1());
show(getFooBar2());
show(getFooBar());
}
}
Both FooBar1
and FooBar2
implement Foo
and Bar
. In main
, the calls to getFooBar1
and getFooBar2
can be assigned to a variable, though there isn't a strong reason for it to know IMHO.
But getFooBar
is the interesting case, which uses a proxy. In practice, it may be the only instance of a an object that implements the two interfaces. A different method (show
here) can be used with a temporary in a type-safer manner, but it cannot be assigned to a variable without the FooBarWrapper
hack described in the question. It is not even possible to create a generic wrapper, class Wrapper<T extends U & V>
is not allowed.
The only trouble seems be defining a syntax, other type checking mechanisms seem to be in place, at least in Oracle javac 1.7.0.
Solution 3
Like @Paul Bellora mentioned in his answer, the type get resolved by the caller, since essentially it will now what it is calling. I would just like to add to his answer with an use case where I think the usage of the syntax could be of benefit.
There are always alternatives that avoid using such syntax. I cannot think of a single example that this is utterly necessary. However I can think of a use case of a particular situation that this syntax could be used conveniently, although I didn't even used it myself. I know its not the best example out there but it can get to the point.
Case
Recently I've been working in the development of an user interface. In this application I use a library to manage my GUI elements. In addition to the features of the library, I created a custom interface that defines a View in my application that has inputs for a specific type of data, lets say, input of coordinates. That interface would look like:
public interface CoordinateView extends View
{
Coordinate getCoordinate();
//Maybe more stuff
}
I have several windows across my application that implement this interface. Now lets say that for some reason I want to store in a model the last coordinate submitted in a window and close the window right after. For this I can attach a handler to the window button that submits the form, the handler will get triggered when the user closes the Window. I could achieve that by simply adding the handler anonymously in every window, like:
public MyWindow extends Window implements CoordinateView, OtherInterface
{
private Button submitButton;
public MyWindow()
{
super();
//Create all the elements
submitButton.addClickHandler(
new ClickHandler()
{
@Override
onCLick(ClickEvent e)
{
getModel().add(getCoordinate());
destroy();
}
});
}
}
However, this design is not desirable for me, it is not modular enough. Considering I have a decent amount of windows with this behavior, changing it could get rather tedious. So I rather extract the anonymous method in a class so that it would be easier to change and maintain. But the problem is that the destroy() method is not defined in any interface, is just part of window and the getCoordinate() method is defined in the interface I defined.
Usage
In this case I could use multiple bounds like the following:
public class MyController <T extends Window & CoordinateView> implements ClickHandler
{
private T windowWithCoordinates;
public MyController (T window)
{
windowWithCoordinates = window;
}
@Override
onClick(ClickEvent e)
{
getModel().add(windowWithCoordinates.getCoordinate());
windowWithCoordinate.destroy();
}
}
Then the code in the windows will now be:
public MyWindow extends Window implements CoordinateView, OtherInterface
{
private Button submitButton;
public MyWindow()
{
super();
//Create all the elements
submitButton.addClickHandler(new MyController<MyWindow>(this));
}
}
Notice that the behaviour will remain the same, the code is just a cohesive as it used to be. Its only more modular, but it didn't required the creation of an additional interface to be able to extract it properly.
Alternative
Alternatively, I could have defined an additional interface extending CoordinateView
and define a method to close the window.
public interface CoordinateWindow extends CoordinateView
{
void destroy();
}
Having the window implement this more specific interface instead of making unnecessary use of generic parameters in the extracted controller:
public class MyController implements ClickHandler
{
private CoordinateWindow windowWithCoordinates;
public MyController (CoordinateWindow window)
{
windowWithCoordinates = window;
}
@Override
onClick(ClickEvent e)
{
getModel().add(windowWithCoordinates.getCoordinate());
windowWithCoordinate.destroy();
}
}
public MyWindow extends Window implements CoordinateWindow
{
private Button submitButton;
public MyWindow()
{
super();
//Create all the elements
submitButton.addClickHandler(new MyController(this));
}
@Override
void destroy()
{
this.destroy();
}
}
This approach for some can be seen as much cleaner than the previous and even more reusable since now it could be added to other "windows" outside of the specified hierarchy. Personally, I prefer this approach as well. However, it may result in a little more coding since a new interface has to be defined just for the sake of getting a access to a desired method.
In conclusion, although I personally don't recommend it I think using generic types with multiple bounds could help in coupling definitions while reducing the amount of code.
Related videos on Youtube
Rafael T
Updated on June 12, 2022Comments
-
Rafael T almost 2 years
I have recently seen that one can declare a return type that is also bounded by an interface. Consider the following class and interface:
public class Foo { public String getFoo() { ... } } public interface Bar { public void setBar(String bar); }
I can declare a return type like this:
public class FooBar { public static <T extends Foo & Bar> T getFooBar() { //some implementation that returns a Foo object, //which is forced to implement Bar } }
If I call that method from somewhere, my IDE is telling me that the return type has the method
String getFoo()
as well assetBar(String)
, but only If I point a dot behind the Function like this:FooBar.getFooBar(). // here the IDE is showing the available methods.
Is there a way to get a reference to such an Object? I mean, if I would do something like this:
//bar only has the method setBar(String) Bar bar = FooBar.getFooBar(); //foo only has the getFoo():String method Foo foo = FooBar.getFooBar();
I would like to have a reference like this (pseudo code):
<T extents Foo & Bar> fooBar = FooBar.getFooBar(); //or maybe $1Bar bar = FooBar.getFooBar(); //or else maybe Foo&Bar bar = FooBar.getFooBar();
Is this possible somehow in Java, or am I only able to declare return types like this? I think Java has to type it also, somehow. I'd prefer not to resort to a wrapper like this, as it feels like cheating:
public class FooBarWrapper<T extends Foo&Bar> extends Foo implements Bar { public T foobar; public TestClass(T val){ foobar = val; } @Override public void setBar(String bar) { foobar.setBar(bar); } @Override public String getFoo() { return foobar.getFoo(); } }
Did Java really invent such a nice feature, but forget that one would like to have a reference to it?
-
Montre over 11 years"I would like to have a pointer like this" - you can't. Variables have to be of a known (possibly erased) type, and a type constraint isn't a type
-
Montre over 11 yearsAlso, you can't actually return anything from
getFooBar()
without casting it to (T), which should give you a hint that you're doing something wrong. -
Montre over 11 years(Oddly enough, this discovered a bug in IntelliJ's code analysis engine, it only recognizes methods of
Foo
as valid.) -
Paul Bellora over 11 yearsCould the downvoter please explain? This is an understandable and relatively well-written question.
-
Paul Bellora over 11 yearsJust to clarify the bounty message, I'm specifically looking for a use case for a syntax like
Foo&Bar bothFooAndBar = ...
- that is usingFoo&Bar
as the type of a variable (like the question is asking for) or a return type.
-
-
Montre over 11 years-1: It won't compile because you broke it more. This works: ideone.com/Sdo0en (It's still broken in other ways but it can be made to compile without changing much.)
-
Rafael T over 11 yearsRight, i just wrote down example classes, which should show my problem better. The changes Paul made should compile better.
-
Miserable Variable over 11 yearsThe wikipedia link link has a horrible (IMHO) explanation of heap pollution. Perhaps you can explain?
-
Paul Bellora over 11 yearsYeah that is a terrible article actually, let me try to find a better link, otherwise I'll edit in a quick example.
-
Miserable Variable over 11 yearsIf there is another
static <U extends Foo & Bar> processFooBar(U u)
I call it directly asprocessFooBar(getFooBar())
. I haven't tried but I suspect the get can return a proxy that implements bothFoo
andBar
(I recoded the OP's example slightly) which means there may not actually be a named type that satisfies both constraints. So I don't understand why you say the caller must know what T is -
Miserable Variable over 11 yearsSee pastebin.com/UGNvgjEZ. I realize this is not very difficult from your examples, but my point is that the client doesn't need to know the actual type
T
. It can call another method on the returned value and can assign it to either aFoo
orBar
reference but not both. Doesn't that seem a syntax hole? -
Paul Bellora over 11 years@MiserableVariable Very interesting. Your example compiles with javac, but not eclipse compiler. Eclipse says (hilariously): "Bound mismatch: The generic method
getFooBar()
of typeFooBar
is not applicable for the arguments()
. The inferred typeFoo
is not a valid substitute for the bounded parameter<T extends Foo & Bar>
". So without any context like this Eclipse is inferring the erasure ofT
forT
. Can't tell what javac is inferring forT
if anything - can't figure out how to get NetBeans to tell me. -
Paul Bellora over 11 years@MiserableVariable Actually even with javac on ideone (sun-jdk-1.6.0.31) I'm getting the "T cannot be determined; no unique maximal instance exists" compile error: ideone.com/3CqrAJ Not sure why it worked with my jdk 1.6.0. I think "T cannot be determined" is more to my point.
-
Paul Bellora over 11 years@MiserableVariable And then again with sun-jdk-1.7.0_03 it passes: ideone.com/YuKQLq :S
-
Paul Bellora over 11 years@MiserableVariable RE: heap pollution, updated link and added my own explanation. Lemme know if it makes sense.
-
Miserable Variable over 11 yearsGood explanation and link. But do you think this is syntax hole? Why does Eclipse infer the type to be Foo? I don't understand the jdk6 error. My test was on 1.7.0
-
Paul Bellora over 11 years@MiserableVariable It's hard to comment without combing the JLS. I think it's a bug for it to compile, because as far as I know
T
should default to its erasureFoo
when it can't be inferred from the arguments or assignment (this often happens with nested generic calls) - in other words I'm siding with Eclipse, which is known to be more strictly correct than javac in some generics situations. It shouldn't be possible forT
to be unknown, since you can't call a generic method with wildcard type arguments, e.g.FooBar.<?>getFooBar()
. -
Miserable Variable over 11 yearsI think I understand what you saying about calling with wildcard type but I don't understand why the erasure is
Foo
when the constraint isextends Foo & Bar
. FWIW Eclipse compiles and runs it for me with 1.6 compliance just fine except for the unchecked warnings. -
Paul Bellora over 11 years@MiserableVariable The erasure of a type parameter with multiple bounds is always (the erasure of) the first bound. See this post: stackoverflow.com/a/8055497/697449
-
kutschkem over 11 yearsIs there a way to get around the heap pollution without having to use Class<T>? Wildcards <? extends Foo & Bar> shouldn't work because then the caller needs to perform an unchecked cast.
-
Paul Bellora over 11 years@kutschkem What this boils down to is not being able to do
new T()
in Java. SogetFooBar
must take some producer ofT
.Class<T>
is just a common example - it has thenewInstance
shortcut andcast
also lets you do a checked cast. Not sure what you're saying about wildcards though. -
Paul Bellora over 11 years@kutschkem Actually IIRC multiple bounds aren't allowed in wildcards for some reason, just type parameters - not sure if that changes what you were getting at.
-
kutschkem over 11 years@Paul ah ok, i didn't know wildcards with multiple bounds aren't allowed. But as i said, and you correctly explained, wildcards with mutliple bounds wouldn't help either.
-
Montre over 11 years@kutschkem The reason
Class<T>
tokens help you get around type pollution is theClass.cast()
method. You're basically replacing the compile-time cast toT
which is unsafe (and can't be made safe) with a runtime check. This moves verifying type consistency to the call site where the compiler can make sure the type parameter of the method and theClass
match. The question "how to get around heap pollution withoutClass<T>
?" doesn't really make sense, because that's the intended way of avoiding it. (The method is new in 1.5) It's like asking "how to declare variables without keywords?". -
Paul Bellora over 11 yearsOoooh, my bounty message must not have been clear. Sorry, but I specifically meant a use case for a syntax like
Foo&Bar bothFooAndBar = ...
, as in usingFoo&Bar
as the type of a variable (like the question is asking for) rather than generic bounds. Sorry, I should have made that more clear. +1 for a great example of using multiple generic bounds though. -
Sednus over 11 yearsOh, I'm sorry I didn't actually answered the question. I thought you meant the syntax in a more general case.
-
Sednus over 11 yearsIf the bounty gets awarded automatically, I can always return it ;)
-
Paul Bellora over 11 yearsHaha, you better or thugs are going to be knocking on your door demanding my points back :)
-
Paul Bellora over 11 years@MiserableVariable Hey it turns out you were right about the caller not having to know what
T
is - check out this example I put together based on the code you shared: ideone.com/V1VED9. I think the earliergetFooBar
example was a weird corner case because it didn't have any arguments to infer from, so some compilers were choking on it and that threw me off. The new example clearly shows that?
can be inferred (but not specified). As demonstrated this provides a nice use case for the hypotheticalFoo&Bar fooBar = ...
syntax. -
Paul Bellora over 11 years@MiserableVariable Anyway I'd appreciate if you posted what we came up with together as an answer and I can give it the bounty :D