4

I guess this is a duplicated question, but after browsing heaps of related questions, I couldn't find a matching one ... yeah, lame excuse ;)

I'm currently developing a common interface for POIs HSLF/XSLF implementations. The reason for using generics is to support the Iterable interface, where user code need not to downcast to the concrete implementation, i.e. one can decide, if he wants to use the implementation classes or the common interface. Of course without generics the return type narrowing works as expected.

My goal is to minimize the parameter declarations for the user of the classes - see main method. Internally the generic references can more complex.

So I'd like to have something like this: (for the sake of simplicity, I haven't used the Iterable interface, but a different type argument)

/* Update: added static to the classes and removed null definitions to actually have a running example */

public class GenericsTest {
    static interface SlideShow {}
    static class HSLFSlideShow implements SlideShow {}

    static interface Notes<SS extends SlideShow> {}
    static class HSLFNotes implements Notes<HSLFSlideShow> {}

    static interface Slide<SS extends SlideShow> {
        <N extends Notes<SS>> N getNotes();
        <N extends Notes<SS>> void setNotes(N n);
    }

    // compile errors
    static class HSLFSlide implements Slide<HSLFSlideShow> {
        HSLFNotes notes = new HSLFNotes();

        @Override
        public HSLFNotes getNotes() { return notes; }
        @Override
        public void setNotes(HSLFNotes n) { notes = n; }
    }

    public static void main(String[] args) {
        HSLFSlide s = new HSLFSlide();
        HSLFNotes n = s.getNotes();
        s.setNotes(n);

        Slide<HSLFSlideShow> s2 = new HSLFSlide();
        Notes<HSLFSlideShow> n2 = s2.getNotes();
    }
}

I could get it to work with ... but this seems a bit clumsy:

    static interface Slide<SS extends SlideShow, N extends Notes<SS>> {
        N getNotes();
        void setNotes(N n);
    }

    static class HSLFSlide implements Slide<HSLFSlideShow,HSLFNotes> {
        HSLFNotes notes = new HSLFNotes();

        @Override
        public HSLFNotes getNotes() { return notes; }
        @Override
        public void setNotes(HSLFNotes n) { notes = n; }
    }

    public static void main(String[] args) {
        HSLFSlide s = new HSLFSlide();
        HSLFNotes n = s.getNotes();
        s.setNotes(n);

        Slide<HSLFSlideShow,HSLFNotes> s2 = new HSLFSlide();
        Notes<HSLFSlideShow> n2 = s2.getNotes();
    }

How would you minimize the needed type parameter in the main method (minimum is JDK6)?

kiwiwings
  • 3,386
  • 1
  • 21
  • 57
  • I believe you can't, at least as long as you want it to be typesafe. The type system in Java is rather verbose and sadly not as flexible as those in some other languages. This is due to the fact that in Java Generics were added later and providing backwards compatibility was important to the language designers. – blalasaadri Apr 03 '15 at 22:22

2 Answers2

3

As far as I can tell, there's no reason to use generic methods here. Overriding a generic method with a non-generic one is probably not doing what you think it's doing. I've discussed this here and here. (See also the generic methods tutorial.)

The type of a generic method is dictated by the call site. Overriding it to be non-generic is allowed for backwards-compatibility reasons. One could still come along and do something like the following:

HSLFSlide slide = ...;
Slide<HSLFSlideShow> oops = slide;

// perfectly legal
// probably throws a ClassCastException somewhere
oops.<SomeOtherNotes>set(new SomeOtherNotes());

Generic methods let us, for example, define a method that returns the same type that it accepts:

static <T> T pass(T t) { return t; }

String s = pass("abc");
Double d = pass(3.14d);

This is the kind of thing generic methods are useful for.

A more typical way to do what you are trying to do would be the following:

interface Slide<SS extends SlideShow> {
    Notes<SS> getNotes();
    void setNotes(Notes<SS> n);
}

class HSLFSlide implements Slide<HSLFSlideShow> {
    Notes<HSLFSlideShow> notes = new HSLFNotes();

    @Override
    public Notes<HSLFSlideShow> getNotes() { return notes; }
    @Override
    public void setNotes(Notes<HSLFSlideShow> n) { notes = n; }
}

If there is some requirement that the reference type of notes can't be the interface, you should take a look at your design again. For example, perhaps the HSLFNotes implementation has features that should be moved to the Notes interface.

Another way would be like the following:

interface Slide<N extends Notes<?>> {
    N getNotes();
    void setNotes(N n);
}

class HSLFSlide implements Slide<HSLFNotes> {
    HSLFNotes notes = new HSLFNotes();

    @Override
    public HSLFNotes getNotes() { return notes; }
    @Override
    public void setNotes(HSLFNotes n) { notes = n; }
}

And also the working example from the question:

interface Slide<SS extends SlideShow, N extends Notes<SS>> {…}
class HSLFSlide implements Slide<HSLFSlideShow, HSLFNotes> {…}

Which is actually fine.

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • First of all, thank you very much - your answer/links gave me more insight in generics, where I still have some understanding problems every now and then ... @"perfectly legal": I've already thought something like this would be possible, but I didn't know the syntax. I guess in the end I've to decide, to what degree I want to be typesafe ... – kiwiwings Apr 04 '15 at 12:50
  • @"there's no reason to use generic methods here": yes, I've already tried to remove all generic declaration, but they somehow start to creep in again, maybe I sacrifice list/iterable-handling and go back to arrays ... when it's finished I'll have a look at the user code and decide how horrible it looks like ;) @"typical way": there will be implementation specific methods and I don't want the user to downcast the references, when he decides to work on implementation references – kiwiwings Apr 04 '15 at 12:51
  • @"another way/working example": when I want to play safe, I think the working example is best then, it locks down the possibility to mix the implementations – kiwiwings Apr 04 '15 at 12:51
1

Although this seems to be an answer, please feel free to add another alternative:

public class GenericsTest {
    static interface SlideShow {}
    static class HSLFSlideShow implements SlideShow {}

    static interface Notes<SS extends SlideShow> {}
    static class HSLFNotes implements Notes<HSLFSlideShow> {}

    static interface Slide<SS extends SlideShow> {
        <N extends Notes<SS>> N getNotes();
        <N extends Notes<SS>> void setNotes(N n);
    }

    static class HSLFSlide implements Slide<HSLFSlideShow> {
        HSLFNotes notes = new HSLFNotes();

        @SuppressWarnings("unchecked")
        @Override
        public HSLFNotes getNotes() {
            return notes;
        }

        @Override
        public <N extends Notes<HSLFSlideShow>> void setNotes(N n) {
            notes = (HSLFNotes)n;
        }
    }

    public static void main(String[] args) {
        HSLFSlide s = new HSLFSlide();
        HSLFNotes n = s.getNotes();
        s.setNotes(n);

        Slide<HSLFSlideShow> s2 = new HSLFSlide();
        Notes<HSLFSlideShow> n2 = s2.getNotes();
    }
}
kiwiwings
  • 3,386
  • 1
  • 21
  • 57