4

Sorry if the title sucks, not sure how to ask this question...

Lets say I have some interface Foo that takes a parameter

  public interface IFoo<Bar extends IBar>{

      public Bar generateBar();
  }

and I have another class that takes a foo object and will generate a bar object. I can define it like this.

 public class FooBar<Bar extends IBar, Foo extends IFoo<Bar>>{

      private Foo foo;

      public FooBar(Foo foo){
          this.foo = foo;
      }

      public Bar getBar(){
           this.foo.generateBar();
      }

This all works fine. However, it seems silly for me to be defining Bar as a parameter of my FooBar class. The IFoo object is going to be parametrized to take the Bar value, so I would assume I could infer the type of Bar from whatever parameter the Foo object has; thus avoiding someone having to provide two parameters every time they define a FooBar object.

Is there a way I can do this? Instead of making Bar part of the FooBar parametrized type just infer it from Foo's parameter?

edit: The answer appears to be this can't be done, as already answered. However, I'm going to leave this open for another day because I'm hoping someone may post an answer that not only if it can be done, but why it is not possible. Specifically, is this simply a use case java developers never chose to support, or is there a reason that it's not possible for the same compile time type checking that supports other parametrization checks to infer Bar type by looking at the specific IFoo type parameter. isn't IFoo parameter able to be determined as well as any parametrized type by the compiler?

dsollen
  • 5,467
  • 5
  • 34
  • 67
  • 1
    yes, this is quite annoying on the use-site. haven't seen any good solution:) In case the use-site happens to not need `getBar()`, you might use type `FooBar, SomeFoo>` instead. – ZhongYu Oct 21 '15 at 17:27

4 Answers4

2

If you care about the actual type parameter to IFoo, then what you have is correct -- declaring the type parameter Bar so it can be the type argument to IFoo when declaring Foo.

However, if you don't care in the code what the type parameter to IFoo is, then you can eliminate the Bar type argument and replace the type argument to IFoo with a wildcard bound.

class FooBar<Foo extends IFoo<? extends IBar>>{

Your getBar method will need to return an IBar.

public IBar getBar(){
    return this.foo.generateBar();
}
rgettman
  • 167,281
  • 27
  • 248
  • 326
  • Good alternative approach. – jgitter Oct 21 '15 at 16:36
  • a good work around in many cases (not mine sadly, I really need to know the type of Bar to make this method useful), but is the implication that I can't maintain knowledge of Bar type without using both parameters? – dsollen Oct 21 '15 at 16:37
  • Creating a second type parameter `Bar` is necessary so that you can maintain knowledge of the type argument to `IFoo`. – rgettman Oct 21 '15 at 16:41
1

In order to have a method that creates a Bar object, you need an object of type Class<Bar>. Then you can use:

class FooBar ...{
    Class<Bar> barClass = ...;

    public Bar getBar(){
         return barClass.newInstance();
    }
}

There a various ways to get barClass. I'm not sure what design to recommend since you have given us arbitrary types Foo, Bar, etc.

A more general solution would be:

class FooBar<T> ...{
        Class<T> someClass = ...;

        public T getInstance(){
             return someClass.newInstance();
        }
    }

You would need to pass Class<Bar> as a parameter somehow.

Jeff Miller
  • 1,374
  • 1
  • 9
  • 18
0

Presumably your getBar method will return a Bar. You can't infer the type due to type erasure. You could return a bare Object for casting, but that rather defeats the purpose of generics.

jgitter
  • 3,296
  • 1
  • 16
  • 25
  • but at compile time I pass a Foo object that is already parametrized. so I've already promised that if I call my Foo.generateBar() it will return a bar, this is known at compile time. Can we not therefore at compile time infer Bar from the parametrization of Foo? – dsollen Oct 21 '15 at 16:39
  • No, it isn't. http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens https://docs.oracle.com/javase/tutorial/java/generics/erasure.html – jgitter Oct 21 '15 at 17:10
  • I understand type erasure, but that is at run time. I'm curious why the parameter check that happens at compile time wouldn't be able to support this. See my edit to the question. I'd really love to understand whats happening at compile time that presumably keeps them from being able to support this. – dsollen Oct 21 '15 at 17:50
0

Answering my own question, just to mention the approach I used in my specific situation, a work around since what I want can't be done. Honestly, it's pretty simple so I should have tried it first, but I got caught up in wondering what can be done with parametrization.

My FooBar equivalent now looks like this:

public class FooBar<Bar extends IBar>{

  private IFoo<Bar> foo;

  public FooBar(IFoo<Bar> foo){
      this.foo = foo;
  }

  public Bar getBar(){
       this.foo.generateBar();
  }
dsollen
  • 5,467
  • 5
  • 34
  • 67