tags:

views:

229

answers:

3

Sometimes in my code I'd like to refer to (what I think of as) the implicit type in a generics heirarchy (basically the type implied by the first ? in the following).

public class Foo<BAR extends Bar<?>> {
  public void t(BAR x) {
    List l = new ArrayList();
    Object baz = x.makeBaz(null);
    l.add(baz);
    x.setBazList(l);
  }
}

public interface Bar<BAZ extends Number> {
  public BAZ makeBaz(Object arg1);
  public List<BAZ> getBazList();
  public void setBazList(List<BAZ> baz);
}

For example in the above code would it be possible to replace the lines

    List l = new ArrayList();
    Object baz = x.makeBaz(null);

with something using generics?

I would prefer to avoid having to write:

public class Foo<BAZ extends Number, BAR extends Bar<BAZ>> {
  public void t(BAR x) {
    List<BAZ> l = new ArrayList<BAZ>();
    BAZ baz = x.makeBaz(null);
    l.add(baz);
  }
}

since it feels unnatural to force this on the declaration of any derived classes.

I know the above is a bit contrived, but it's a lot simpler than trying to show the actual code I'm looking at.

+3  A: 

I guess the main problem is that you need to somehow enforce that the type you get out of x.makeBaz() is the same as the type parameter of the list you give to x.setBazList(l), and that if you just use ? you lose that enforcement ability, so you need a generic type parameter. But in a sense the type parameter is only needed "locally" in those few lines and the rest of the class shouldn't need to know.

You could use the type parameter BAZ to parameterize just the method instead of the class:

public <BAZ extends Number> void t2(Bar<BAZ> x) {
  List<BAZ> l = new ArrayList<BAZ>();
  BAZ baz = x.makeBaz(null);
  l.add(baz);
  x.setBazList(l);
}

This serves to localize the region of code where we need this temporary type variable BAZ. Note that this method's signature is effectively public void t2(Bar<?> x), i.e. the outside doesn't need to know about this BAZ.

Now of course this method now accepts all Bar<anything> instead of just BAR. If this is an issue, you could just "wrap" the above method. We don't have the same problem as before because all the BAZ stuff is already enclosed inside t2(), which takes a Bar<anything>, so we can safely pass a BAR:

public void t(BAR x) {
  t2(x);
}

I agree that this seems kind of roundabout. Maybe someone else will come up with a better way.

newacct
by the way this is called "capture helper": http://www.ibm.com/developerworks/java/library/j-jtp04298.html#3.0
newacct
A: 

You can at least bind to Number as BAZ extends Number

public class Foo<BAR extends Bar<?>> {
    public void t(BAR x) {    
     List<Number> l = new ArrayList<Number>();
     Number baz = x.makeBaz(null);
     l.add(baz);
     // l.add(Integer.valueOf(1)); compiles but with this here the next line throws ClassCastException
     x.setBazList(l);
    }
}

public interface Bar<BAZ extends Number> {
    public BAZ makeBaz(Object arg1);
    public List<BAZ> getBazList();
    public void setBazList(List<BAZ> baz);
}

public class BarConcreate implements Bar<BigDecimal>{
    long counter = 1;

    private List<BigDecimal> list = new ArrayList<BigDecimal>();
    public List<BigDecimal> getBazList() {
     return list;
    }


    public BigDecimal makeBaz(Object arg1) {
     return BigDecimal.valueOf(counter++);
    }

    public void setBazList(List<BigDecimal> baz) {
     list = baz;   
     for(BigDecimal d : baz){
      logger.debug("value:" + d.toString());
     }
    }

}


public void TestFooBar(){
    Foo<Bar<BigDecimal>> foo = new Foo<Bar<BigDecimal>>();
    Bar<BigDecimal> bar = new BarConcreate();
    foo.t(bar);
}

This java compiles and runs if the List is only filled with BigDecimal every thing works if the List has an integer in it we get a class cast exception on the line x.setBazList(l);

I find that very interesting.

David Waters
that will not type-check, as x.setBazList() wants a List<BAZ> not a List<Number>
newacct
It compiles and runs, which I find a little strange but I did check before adding the answer.
David Waters
A: 

Hi, I would go for this if you like it:

List<Number> l = new ArrayList<Number>();
Number baz = x.makeBaz(null);

Hope this helps.

EDIT: Also this makes sense:

public class Foo<BAR extends Bar<? extends Number>>
David Santamaria
that will run into problems when you get to x.setBazList(l)
newacct