32

I'm working with a TreeTable and when changing cell factory I am required to pass a

Callback<TreeTableColumn<A, capture of ?>, TreeTableCell<A, capture of ?>>

where A is a class I am working with but I have no idea how to work with the "capture of ?"

I tried to create

new Callback<TreeTableColumn<A, ?>, TreeTableCell<A, ?>>

but IDEA shows warning

setCellFactory(Callback<TreeTableColumn<A, capture<?>>, TreeTableCell<A, capture<?>>>) in TreeTableColumn cannot be applied to (anonymous Callback<TreeTableColumn<A, ?>, TreeTableCell<A, ?>>)

I tried using specific classes (like String) instead of "?" as well, but nothing helped.

Could anyone explain to me how to work with this?

Thank you.

EDIT:

I gathered a little bit more information.. the CellFactory of TreeTableColumn<S,T> should be Callback<TreeTableColumn<S,T>,TreeTableCell<S,T>>, however, the TreeTableColumn that I'm working with is created as a raw type (in a library).

Using a raw type Callback works. But are there any other options how to work this around?

roeygol
  • 6,664
  • 6
  • 43
  • 75
kotycheese
  • 323
  • 1
  • 3
  • 6
  • The `?` means the compiler doesn't know what the type is. When it doesn't know what the type is it can't match another unknown time. It's not like a wildcard which accepts anything. – Peter Lawrey Oct 24 '16 at 11:34
  • 1
    `I tried using specific classes (like String) instead of "?" as well, but nothing helped.` - You might want to show what exactly you tried and you might want to include how you defined that anonymous instance, i.e. the code. – Thomas Oct 24 '16 at 11:38
  • 1
    what is the signature of `setCellFactory`? – newacct Oct 26 '16 at 01:34
  • It's here http://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeTableColumn.html – kotycheese Oct 26 '16 at 11:05
  • and the column was created like this, without generics: TreeTableColumn col = new TreeTableColumn("name")); – kotycheese Oct 26 '16 at 11:09
  • 3
    @kotycheese: You need to edit the information from that last comment into your question, since that's the key to your problem! You're using a *raw type* which disables generics entirely. (It is mostly equivalent to erasing all generic type parameters to their erasure, e.g. `Object`, but there are important differences.) If you consistently use properly parameterized types then this problem should go away. See this question for more information: http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it – Daniel Pryden Oct 27 '16 at 08:04

2 Answers2

22

A wildcard represents an unknown type.

wildcard capture is the process of binding the value of a wildcard type to a new type variable. For example:

List<?> list = ...;
shuffle(list);

where

<T> void shuffle(List<T> list) {
    ...
}

Here, the unknown value of ? is bound to the new type variable T upon invocation of the shuffle method, allowing the shuffle method to refer to that type.

The Java compiler internally represents the value of a wildcard by capturing it in an anonymous type variable, which it calls "capture of ?" (actually, javac calls them "capture #1 of ?" because different uses of ? may refer to different types, and therefore have different captures).

Ok, so what is wrong in your code? You are trying to invoke a method

<S,T> setCellFactory(Callback<TreeTableColumn<S,T>, TreeTableCell<S,T>> factory);

with

Callback<TreeTableColumn<S,?>, TreeTableCell<S, ?>> factory;

In the method signature, the type parameter T stands for a single type, that must be provided by the caller. As a convenience, the compiler automatically attempts to infer a suitable value (-> type inference). Your compilation error means that the compiler was unable to do so.

In this instance, this is not a shortcoming of type inference, as it is actually impossible to assign a suitable value to T, because both ? need to be subtypes of T, but the compiler can not know that the two ? stand for the same type, or even related types.

To successfully invoke this method, your argument type must use the same type for all occurrences of T. If you already have such a type at hand, go ahead and use it. Otherwise, you may be able to introduce one using wildcard capture:

setCellFactory(newFactory());

where

<S,T> Callback<TreeTableColumn<S,T>, TreeTableCell<S,T>> newFactory() {
    return new Callback<TreeTableColumn<S,T>, TreeTableCell<S,T>> {
        ...
    }
}
meriton
  • 61,876
  • 13
  • 96
  • 163
2

from what I found from what is a capture conversion and oracle capture generic docs it looks like you are facing some problem where the compiler can't find the needed helper class, or tries to place Object in there, but what you give him can not be converted safely.

EDIT:

DEFAULT_CELL_FACTORY

public static final Callback<TreeTableColumn<?,?>,TreeTableCell<?,?>>

If no cellFactory is specified on a TreeTableColumn instance, then this one will be used by default. At present it simply renders the TableCell item property within the graphic property if the item is a Node, or it simply calls toString() if it is not null, setting the resulting string inside the text property.

and

setCellFactory

public final void setCellFactory(Callback<TreeTableColumn<S,T>,TreeTableCell<S,T>> value)

Sets the value of the property cellFactory. Property description: The cell factory for all cells in this column. The cell factory is responsible for rendering the data contained within each TreeTableCell for a single TreeTableColumn. By default TreeTableColumn uses a default cell factory, but this can be replaced with a custom implementation, for example to show data in a different way or to support editing. There is a lot of documentation on creating custom cell factories elsewhere (see Cell and TreeTableView for example).

Finally, there are a number of pre-built cell factories available in the javafx.scene.control.cell package.

taken out from Java 8 API Doc.

so something like this should be going more into the right direction:

public static <S> Callback<TableColumn<S,String>, TableCell<S,String>> forTableColumn() {
   return forTableColumn(new DefaultStringConverter());
}

or something like this depending on what excactly you want to give into the Cell.

setCellFactory(TextFieldTableCell.<DataModel, Integer>forTableColumn(new IntegerStringConverter()));

these code snippets are taken from This StackOverflow Thread

and last this link could help you too: TableView Cell Tutorial

So this should give you some more light in the dark about what might be causing the problem.

Community
  • 1
  • 1
Nico
  • 1,508
  • 1
  • 17
  • 36
  • Do you have any idea what I could do to make it work? – kotycheese Oct 25 '16 at 13:00
  • I updated my answer to try and help you with your problem. I never used JavaFX or TreeTableView's so I just can help by researching and reading documentations :) Hope this helps you @kotycheese – Nico Oct 27 '16 at 06:31
  • Thank you very much, you helped me a lot. I updated my question and added more information, does it help figure out the problem? – kotycheese Nov 03 '16 at 11:25