1

I have created a Custom Table Cell in JavaFX, pursuant to this answer, so I can have different font styling for different parts of the cell's text.

I use this Custom Table Cell on two different types of TableViews:TableView<Track> and TableView<Album>.

Both Track and Album implement the Interface AlbumInfoSource:

public interface AlbumInfoSource {
    public String getAlbumTitle();
    public String getFullAlbumTitle();
    public String getReleaseType();
    public String getDiscSubtitle();
    public Integer getDiscCount();
    public Integer getDiscNumber();

}

My Custom TableCell is typed with that AlbumInfoSource, so it can render cells for both a TableView<Album> and a TableView<Track>.

Here is the basic code:

public class FormattedAlbumCell<T, S> extends TableCell <AlbumInfoSource, String> {

    private TextFlow flow;
    private Label albumName, albumType, albumDisc;

    public FormattedAlbumCell () {
        /* Do constructor stuff */
    }

    @Override
    protected void updateItem ( String text, boolean empty ) {
        super.updateItem ( text, empty );
        /* Do pretty rendering stuff */
    }
}

And then I apply it to a column like this:

TableColumn<Album, String> albumColumn;
albumColumn = new TableColumn<Album, String>( "Album" );
albumColumn.setCellFactory( e -> new FormattedAlbumCell () );

Which works perfectly fine, but I get a warning on that last line, which says:

Warning: FormattedAlbumCell is a raw type. References to generic type FormattedAlbumCell< T ,S > should be parameterized

If I change my FormattedAlbumCell class such that it extends TableCell <Album, String>, then the warning goes away. But then I can't use the FormattedAlbumCell for a TableView<Track>, I would have to duplicate the FormattedAlbumCell class make it extend TableCell, which seems dumb to me.

Is there a way to get these parameters straight without creating two separate classes? It seems like the issue comes from the paramaterizing stuff having trouble with Interfaces.

Grumblesaurus
  • 3,021
  • 3
  • 31
  • 61

1 Answers1

7

Your FormattedAlbumCell has two generic type parameters (<T, S>) which, by the looks of things, are completely unused. Remove them.

class FormattedAlbumCell<T, S> extends TableCell<AlbumInfoSource, String>

becomes

class FormattedAlbumCell extends TableCell<AlbumInfoSource, String>

Your next problem is that generics are invariant. A TableCell<AlbumInfoSource, String> is not a TableCell<Album, String>, or vice-versa.

If you need to be able to create a TableColumn<Album, String> in your method then you need a TableCell<Album..., not a TableCell<AlbumInfoSource.... But you also want this to work for other implementations of AlbumInfoSource, so changing the cell won't work.

This means that you need to introduce another level of indirection via generics.

class FormattedAlbumCell<T extends AlbumInfoSource> extends TableCell<T, String>

This says that we can create different generic versions of FormattedAlbumCell, subject to the constraint that T is a more specific type of AlbumInfoSource (i.e. extends or implements it).

Now we can create a FormattedAlbumCell<Track> which would be a TableCell<Track, String>, or we can create a FormattedAlbumCell<Album> which will be a TableCell<Album, String>.

See also Oracle's generics tutorial

Michael
  • 41,989
  • 11
  • 82
  • 128
  • This causes an error on the line that is currently giving a warning -- "Type mismatch: cannot convert from FormattedAlbumCell to TableCell" – Grumblesaurus Nov 12 '18 at 09:02
  • Album implements AlbumInfoSource. FormattedAlbumCell is used by a handful of different TableViews, all of which display Classes which implement AlbumInfoSource. The difficulty seems to be in sorting that out with parameters. Your comment about copy/paste isn't helpful. – Grumblesaurus Nov 12 '18 at 09:09
  • Yea, that appears to be the thing that's causing the warning. Thank you. Given that the code works and creating a duplicated class seems undesirable (see clarifying edits to my original post) what should I do? Just suppress the warning and move on? Refactor and accept duplicated code? Something else? – Grumblesaurus Nov 12 '18 at 09:21
  • I can't change `albumColumn` -- then I'd also have to change `albumTable`, and in other places it expects a full `Album`, not just the narrow `AlbumInfoSource`. If I change `FormattedAlbumCell` to `TableCell – Grumblesaurus Nov 12 '18 at 09:27
  • Sweet! That did it. I also had to change `setCellFactory` as follow: `e -> new FormattedAlbumCell<>`. Can you edit your answer to this solution and, if you're up for it, why this works? Then I can accept it. Thank you! – Grumblesaurus Nov 12 '18 at 09:31
  • @JoshuaD Sure, done. And yes, now that `FormattedAlbumCell` is generic you will need to use the generic type. In this case, the compiler can infer that you want an `Album` due to the type of the column, so the diamond operator is sufficient – Michael Nov 12 '18 at 09:40