Generics ?

K

Knute Johnson

Using Java 7, given the class file:

import javax.swing.*;

public class KList extends JList {
ListModel model = new DefaultListModel();

public KList() {
setModel(model);
}
}

compiling it gives me

C:\com\knutejohnson>javac -Xlint:unchecked Klist.java
Klist.java:7: warning: [unchecked] unchecked call to
setModel(ListModel<E>) as a
member of the raw type JList
setModel(model);
^
where E is a type-variable:
E extends Object declared in class JList
1 warning

How do you extend this class with generic types?

Thanks,
 
T

Tassilo Horn

Hi Peter,
Concrete:

// "int" is just for example…could be anything
public class KList extends JList<int>
{

Now you've baffled me. First, I thought that in JDK 7 you could have
primitive type parameters, but alas it turned out that it is a typo on
your side.

Bye,
Tassilo
 
R

Roedy Green

public class KList extends JList {
ListModel model = new DefaultListModel();

If you were just using JList by itself you would say:

final JList<String> flavour = new JList<String>( new String[] {
"strawberry", "chocolate", "vanilla" } );

Your new class KList can have a built-in fixed contained type, e.g.
String or it can have a variable one.

The first is pretty obvious :

class KList extends JList<String>

The second you can discover by looking for some similar Sun code in
src.zip, e.g.

public interface MutableComboBoxModel<E> extends ComboBoxModel<E> {

so I would think

KList <E> extends JList<E>
should work.

For additional hints, see http://mindprod.com/jgloss/generics.html
and links

I have not tested this and quite commonly things in generics I think
should work do not. Take this just as a hint of something to try.
--
Roedy Green Canadian Mind Products
http://mindprod.com
For me, the appeal of computer programming is that
even though I am quite a klutz,
I can still produce something, in a sense
perfect, because the computer gives me as many
chances as I please to get it right.
 
K

Knute Johnson

public class KList extends JList {
ListModel model = new DefaultListModel();

If you were just using JList by itself you would say:

final JList<String> flavour = new JList<String>( new String[] {
"strawberry", "chocolate", "vanilla" } );

Your new class KList can have a built-in fixed contained type, e.g.
String or it can have a variable one.

The first is pretty obvious :

class KList extends JList<String>

The second you can discover by looking for some similar Sun code in
src.zip, e.g.

public interface MutableComboBoxModel<E> extends ComboBoxModel<E> {

so I would think

KList<E> extends JList<E>
should work.

For additional hints, see http://mindprod.com/jgloss/generics.html
and links

I have not tested this and quite commonly things in generics I think
should work do not. Take this just as a hint of something to try.

Yeah, unfortunately that doesn't work. Is it possible to extend JList
and not get unchecked warnings?
 
R

Roedy Green

Yeah, unfortunately that doesn't work. Is it possible to extend JList
and not get unchecked warnings?

The way I usually crack these sorts of problem is to search my own
code, then src.zip for something with analogous structure to what I am
attempting and do a monkey see, monkey do.

Like all computer stuff, all it takes is a stray comma to blow it up
completely.

You can also start with the shell and gradually add methods.
--
Roedy Green Canadian Mind Products
http://mindprod.com
For me, the appeal of computer programming is that
even though I am quite a klutz,
I can still produce something, in a sense
perfect, because the computer gives me as many
chances as I please to get it right.
 
J

John B. Matthews

Knute Johnson said:
Using Java 7, given the class file:

import javax.swing.*;

public class KList extends JList {
ListModel model = new DefaultListModel();

public KList() {
setModel(model);
}
}

I defined a generic subclass of JList:

class KList<E> extends JList<E> {

public KList(ListModel<E> model) {
this.setModel(model);
}
}

Then I created a ListModel<String>:

DefaultListModel<String> dlm = new DefaultListModel<>();
dlm.add(...);

Then I used it to create a new KList:

KList<String> list = new KList<>(dlm);

Here's my sscce:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.*;

/** @see http://stackoverflow.com/a/5372169/230513 */
public class JakesList {

private static final DateFormat df =
new SimpleDateFormat("dd-MMM-yyyy");

private static class KList<E> extends JList<E> {

public KList(ListModel<E> model) {
this.setModel(model);
}
}

private static void createAndShowGUI() {
DefaultListModel<String> dlm = new DefaultListModel<>();
for (int i = 0; i < 10; i++) {
GregorianCalendar knownDate = new GregorianCalendar();
knownDate.add(Calendar.DAY_OF_MONTH, 3 * i);
dlm.add(i, df.format(knownDate.getTime()));
}
KList<String> list = new KList<>(dlm);
JOptionPane.showMessageDialog(null, list);
}

public static void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
createAndShowGUI();
}
});
}
}
 
K

Knute Johnson

Please be more specific. In what way does it "not work"? Roedy's
suggestion is basically an exact copy of mine, and is correct, provided
you use the suggestion correctly.

Since you didn't post a proper code example showing what you tried,
there's no way to know for sure why it didn't work. But we can say for
sure that what you tried did not match either of the code examples I
provided in my first reply (not counting my goof of using "int" instead
of "Integer" as an example type parameter), since the techniques shown
in those examples _do_ work.

Pete

Example #1

import javax.swing.*;

public class KList<String> extends JList<E> {
private final DefaultListModel<String> model = new
DefaultListModel<String>();

public KList() {
setModel(model);
model.addElement("test");
}
}

C:\com\knutejohnson>javac KList.java
KList.java:3: error: cannot find symbol
public class KList<String> extends JList<E> {
^
symbol: class E
KList.java:8: error: method addElement in class DefaultListModel<E>
cannot be ap
plied to given types;
model.addElement("test");
^
required: String
found: java.lang.String
reason: actual argument java.lang.String cannot be converted to
String by meth
od invocation conversion
where String,E are type-variables:
String extends Object declared in class KList
E extends Object declared in class DefaultListModel
2 errors


Example #2 Roedy's second example that he thought should work

import javax.swing.*;

public class KList<E> extends JList<E> {
private final DefaultListModel<E> model = new DefaultListModel<E>();

public KList() {
setModel(model);
model.addElement("test");
}
}

C:\com\knutejohnson>javac Klist.java
Klist.java:8: error: method addElement in class DefaultListModel<E#2>
cannot be
applied to given types;
model.addElement("test");
^
required: E#1
found: String
reason: actual argument String cannot be converted to E#1 by method
invocation
conversion
where E#1,E#2 are type-variables:
E#1 extends Object declared in class KList
E#2 extends Object declared in class DefaultListModel
1 error

Example #3 only gives a warning

import javax.swing.*;

public class KList extends JList {
private final DefaultListModel<String> model = new
DefaultListModel<String>();

public KList() {
setModel(model);
model.addElement("test");
}
}

C:\com\knutejohnson>javac KList.java
Note: KList.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

C:\com\knutejohnson>javac -Xlint:unchecked KList.java
KList.java:7: warning: [unchecked] unchecked call to
setModel(ListModel<E>) as a
member of the raw type JList
setModel(model);
^
where E is a type-variable:
E extends Object declared in class JList
1 warning

Example #4 Roedy's first suggestion compiles without warning but it is
no longer generic. Which brings me back to my real question, can you
extend a generic class and still be generic?

import javax.swing.*;

public class KList extends JList<String> {
private final DefaultListModel<String> model = new
DefaultListModel<>();

public KList() {
setModel(model);
model.addElement("test");
}
}
 
M

markspace

Which brings me back to my real question, can you
extend a generic class and still be generic?


Yes of course.

model.addElement("test");
^
required: E#1
found: String
reason: actual argument String cannot be converted to E#1 by method
invocation conversion


The problem is that you don't *have* a generic type. You have a
parametrized type of String. Otherwise, you can't stick a String in
that thing. Which is why the compiler is complaining.

Roedy's example compiles because it matches what you did. It
parametrizes the type to String because that's what you have.

No, you cannot have a "generic" class, then assume that you can also use
"string" as a type. Generics specifically prevent that.

You could put a bound on the type of E, like <E super String> and I
think that would work (I didn't test it). But since that is basically
String, CharSequence, or Object, it's really kinda limiting while being
baroque at the same time. I'd just use "String" or "CharSequence" or
"Object" unless you're really sure that the ability to parametrize that
type is really important.
 
K

Knute Johnson

Knute Johnson said:
Using Java 7, given the class file:

import javax.swing.*;

public class KList extends JList {
ListModel model = new DefaultListModel();

public KList() {
setModel(model);
}
}

I defined a generic subclass of JList:

class KList<E> extends JList<E> {

public KList(ListModel<E> model) {
this.setModel(model);
}
}

Then I created a ListModel<String>:

DefaultListModel<String> dlm = new DefaultListModel<>();
dlm.add(...);

Then I used it to create a new KList:

KList<String> list = new KList<>(dlm);

Here's my sscce:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.*;

/** @see http://stackoverflow.com/a/5372169/230513 */
public class JakesList {

private static final DateFormat df =
new SimpleDateFormat("dd-MMM-yyyy");

private static class KList<E> extends JList<E> {

public KList(ListModel<E> model) {
this.setModel(model);
}
}

private static void createAndShowGUI() {
DefaultListModel<String> dlm = new DefaultListModel<>();
for (int i = 0; i< 10; i++) {
GregorianCalendar knownDate = new GregorianCalendar();
knownDate.add(Calendar.DAY_OF_MONTH, 3 * i);
dlm.add(i, df.format(knownDate.getTime()));
}
KList<String> list = new KList<>(dlm);
JOptionPane.showMessageDialog(null, list);
}

public static void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
createAndShowGUI();
}
});
}
}

Thanks very much John, that got me going in the right direction.
 
K

Knute Johnson

Yes of course.




The problem is that you don't *have* a generic type. You have a
parametrized type of String. Otherwise, you can't stick a String in that
thing. Which is why the compiler is complaining.

Roedy's example compiles because it matches what you did. It
parametrizes the type to String because that's what you have.

No, you cannot have a "generic" class, then assume that you can also use
"string" as a type. Generics specifically prevent that.

You could put a bound on the type of E, like <E super String> and I
think that would work (I didn't test it). But since that is basically
String, CharSequence, or Object, it's really kinda limiting while being
baroque at the same time. I'd just use "String" or "CharSequence" or
"Object" unless you're really sure that the ability to parametrize that
type is really important.

Thanks for that and to John for giving me direction to follow.

What I have learned about generics:

You can't have a generic class and then attempt to use non-generic
parameters. You can extend a parameterized(?) generic class (eg.
JList<String>) and do what I was trying to do to it but then you are
limited to String types, which is what I really needed anyway.

I did learn an interesting thing about JList however and that is that
the no-arg constructor creates an anonymous class for it's read-only
ListModel. I've got to look at the source and see what's up with that.

This has only come up because I'm trying to compile some old code that
was originally created in 1.5 and 1.6 with the new 1.7 compiler.

Thanks again everybody for the pointers.
 
K

Knute Johnson

[...]
Example #4 Roedy's first suggestion compiles without warning but it is
no longer generic. Which brings me back to my real question, can you
extend a generic class and still be generic?

As "markspace" says: of course you can.

But you have to do it correctly. (It also helps if you don't keep your
code examples secret, such as having a statement that calls
"model.addElement()" that is important to you, but which you don't
actually share with the rest of us).

None of your examples actually followed the examples I provided
precisely. In each case, you made a mistake:

• example #1: you tried to declare a class name as if it had a generic
type parameter, but used "String" as if it were the name of the parameter.

• example #2: the declaration of the class is fine, but then in your
code you made the illegal assumption that the type parameter "E" is
actually "String", by trying to pass an instance of String to the
addElement() method.

• example #3: you failed to specify "String" as the type parameter for
the base class, meaning you're extending it without generic support,
hence the "unchecked" warning.

• example #4: you can't make a class generic, but at the same time fix
the type parameter as something specific.

Frankly, I think example #4 is the most likely to be what you want. You
appear to want to explicitly pass a string literal to the supporting
types within your own derived class. If that's the case, then you have
made the choice of the type parameter already. It _has_ to be String.

If you actually want to declare a generic type yourself (e.g. so that
you can reuse it generically), then you are not permitted to make any
assumptions about the type parameter's value. That includes not passing
string literals to a generic member, such as your example of the call to
addElement().

Pete

Thanks Pete. That's obvious now but wasn't yesterday :). You know I
looked all over the net for examples of extending a generic class and
found none. It could be that I didn't know what I was looking for really.
 
E

Eric Sosman

[...]
Thanks Pete. That's obvious now but wasn't yesterday :). You know I
looked all over the net for examples of extending a generic class and
found none. It could be that I didn't know what I was looking for really.

Next time something of this sort arises, it may be helpful to
ask "Does Java itself provide an example of what I'm trying to do?"
In the case at hand, you might have found

ArrayList<E> extends AbstractList<E> implements List<E> ...

AbstractList<E> extends AbstractCollection<E> ...

AbstractCollection<E> implements Collection<E> ...

.... illustrating how a generic class can extend a generic class
and/or implement a generic interface.

The wrong thing to do with the Java source is to ask "If a JFoo
has multiple FooListeners, which is the first to be sent a FooEvent?"
because that's the sort of implementation detail that might change
without notice. But one of the right things to do with Java source
is to look at the JFoo implementation to see how it dispatches to the
FooListeners, so you can use that pattern as an example when writing
the Knute class to dispatch KnuteEvents to KnuteListeners.
 
D

Daniel Pitts

Thanks for that and to John for giving me direction to follow.

What I have learned about generics:

You can't have a generic class and then attempt to use non-generic
parameters. You can extend a parameterized(?) generic class (eg.
JList<String>) and do what I was trying to do to it but then you are
limited to String types, which is what I really needed anyway.
Incorrect. You can extend a generic class and narrow (or keep) the
generic parameters..

public class KList<E extends Serializable> extends JList<E> {
}

KList<String> foo;
KList<Integer> bar;
KList<MySerializable> baz;

You can also make concrete the parameter:

public class KListOfStrings extends JList<String> {
}

FWIW, you probably shouldn't be extending JList anyway. You should be
implementing ListModel and/or adding listeners to a JList. Pretty much
the only Swing "J*" class you ever really need to extend is JComponent.
 
L

Lew

Qu0ll said:
But what if you want a special type of JList or JPanel etc. that you can
just drop in wherever a JComponent would be accepted in multiple places in
your program or other programs? How can you avoid subclassing in those
cases?

"Prefer composition to inheritance" does not mean "Use composition instead of
inheritance".

You have to weigh the substantial cost of extending a type over the lower cost
of composing it to see if the benefits justify the expense.

In some cases you actually are not adding any functionality to a candidate
parent type. You think you are, but if all the action happens in 'add()'-style
method (as is often the case with proposed inheritance factoring) in your
derived class with no actual *functional* changes from the parent, then you are
not really inheriting, you're just using 'extends' improperly.

As with everything else in programming, you have to use advice judiciously, not
follow it superstitiously.

Without a code example (SSCCE) we cannot tell if your hypothetical situation
calls for inheritance or not. It's just too broadly stated.
 
M

markspace

in message

But what if you want a special type of JList or JPanel etc. that you can
just drop in wherever a JComponent would be accepted in multiple places
in your program or other programs? How can you avoid subclassing in
those cases?


Well, the first thing you should do is to read the documentation
*carefully* to make sure that what you want to do isn't implemented
already. Sometimes there's a helper class or method that instantiates
common implementations for you, with out you having to do anything.

In the specific case of JPanel, it is not generic, and always accepts
objects of type Component. (There's that readin' documentation thing; I
wasn't sure until I looked it up.) There's no way to change that, as
the Java rules of inheritance preclude it.

For JList, if you really just need a type of JComponent (or I'd
recommend Component), then just make a JList<JComponent>. You don't
need to subclass to get that, you just parametrize the type. That's why
generics are an improvement, they don't require sub-classing.

Don't overlook <?> (unknown type) or just parametrize with type of
Object, when appropriate.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top