Yet another generics question: Needs unchecked conversion to conform to ...

M

Michel T.

I spent a lot of time massaging the code below to avoid a warning,
without success.

I pasted a sample class and inner classes that shows what I want to do.
I wrote two versions of a factory method: one that does not generate
any warnings, and the other generates one.
The return type of the second method is defined like this:

BeanFactory<Bean> createFactory2(String className){
...
}

The returned BeanFactory is also parametized, and this is where my
problem lies. Can anyone suggest how to avoid the warning, besides
adding a @suppresswarning annotation?

public class Generics {

public BeanFactory createFactory1(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});

return cstr.newInstance(new Object[]{});
}

public BeanFactory<Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});

// The following line causes the warning:
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}
 
T

Tom Hawtin

Michel said:
public BeanFactory<Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});

// The following line causes the warning:
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

My first suggestion is not to use reflection.

If not, does BeanFactory<? extends Bean> work for you? (There is no
guarantee that the T in a subclass of BeanFactory is exactly Bean.)

Alternatively, create a bean factory factory. (But stick with using a
Constructor rather than evil Class.newInstance.)

Tom Hawtin
 
M

Michel T.

My first suggestion is not to use reflection.

I have to use reflection. This example is a very-reduced sample that
does not reflect the reality where this is required. I created this
simple class to show the warning.
If not, does BeanFactory<? extends Bean> work for you? (There is no
guarantee that the T in a subclass of BeanFactory is exactly Bean.)

I would not mind changing the return value of createFactory2 to

BeanFactory<? extends Bean> createFactory2(String beanClass)

but I get the same warning.


Also, I tried the following but it does not compile (thus it must not
make sense either):

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory<? extends Bean>> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory<? extends Bean>> cstr =
bfc.getConstructor(new Class[]{});

return cstr.newInstance(new Object[]{});
}

Alternatively, create a bean factory factory. (But stick with using a
Constructor rather than evil Class.newInstance.)

I must use reflection to instantiate the factory. Otherwise, I would
not be posting that question ;)
 
D

Daniel Pitts

Michel said:
My first suggestion is not to use reflection.

I have to use reflection. This example is a very-reduced sample that
does not reflect the reality where this is required. I created this
simple class to show the warning.
If not, does BeanFactory<? extends Bean> work for you? (There is no
guarantee that the T in a subclass of BeanFactory is exactly Bean.)

I would not mind changing the return value of createFactory2 to

BeanFactory<? extends Bean> createFactory2(String beanClass)

but I get the same warning.


Also, I tried the following but it does not compile (thus it must not
make sense either):

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory<? extends Bean>> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory<? extends Bean>> cstr =
bfc.getConstructor(new Class[]{});

return cstr.newInstance(new Object[]{});
}

Alternatively, create a bean factory factory. (But stick with using a
Constructor rather than evil Class.newInstance.)

I must use reflection to instantiate the factory. Otherwise, I would
not be posting that question ;)

*Why* do you need to use reflection? I've found that in most
circumstances, you don't need to use it, even if you *think* you do.

Moving forward, assuming that you do indeed need to do it this way...
what is the exact warning you get? Can you post an sscce
<http://physci.org/codes/sscce/> that allows us to reproduce the
problem?

Anyway, good luck.
 
M

Michel T.

Thanks for your replies,

Daniel Pitts a écrit :
*Why* do you need to use reflection? I've found that in most
circumstances, you don't need to use it, even if you *think* you do.

I'll explain in an upcoming separate message.
Moving forward, assuming that you do indeed need to do it this way...
what is the exact warning you get? Can you post an sscce
<http://physci.org/codes/sscce/> that allows us to reproduce the
problem?

The exat warning is:

Type safety: The expression of type capture-of ? extends
Generics.BeanFactory needs unchecked conversion to conform to
Generics.BeanFactory<? extends Generics.Bean>

The "ssce" (thank you for the info on this acronym) was there in the
first message: The "Generics" class compiles as is without any
dependanies, and the warning shows up in Eclipse (I did not try on any
other compiler). Here it is again with a slight modification in the
return type of the createFactory2 method :

public class Generics {

public BeanFactory createFactory1(String beanClass) throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
return cstr.newInstance(new Object[]{});
}

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
// The following line causes a warning
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}
 
D

Daniel Pitts

Michel said:
Thanks for your replies,

Daniel Pitts a écrit :
*Why* do you need to use reflection? I've found that in most
circumstances, you don't need to use it, even if you *think* you do.

I'll explain in an upcoming separate message.
Moving forward, assuming that you do indeed need to do it this way...
what is the exact warning you get? Can you post an sscce
<http://physci.org/codes/sscce/> that allows us to reproduce the
problem?

The exat warning is:

Type safety: The expression of type capture-of ? extends
Generics.BeanFactory needs unchecked conversion to conform to
Generics.BeanFactory<? extends Generics.Bean>

The "ssce" (thank you for the info on this acronym) was there in the
first message: The "Generics" class compiles as is without any
dependanies, and the warning shows up in Eclipse (I did not try on any
other compiler). Here it is again with a slight modification in the
return type of the createFactory2 method :

public class Generics {

public BeanFactory createFactory1(String beanClass) throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
return cstr.newInstance(new Object[]{});
}

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
// The following line causes a warning
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}

Oh, okay.

There isn't a way to remove all warnings in this case.
Whenever you use reflection to create an object, erasure prevents
anyone from knowing what generic type it has. You'll have to use a
@SupressWarning, or just live with the warning.
 
T

Tom Hawtin

Michel said:
I have to use reflection. This example is a very-reduced sample that
does not reflect the reality where this is required. I created this
simple class to show the warning.


I would not mind changing the return value of createFactory2 to

BeanFactory<? extends Bean> createFactory2(String beanClass)

but I get the same warning.

Rather strangely it requires an intermediate variable. I have no idea
why. Captures are odd.

import java.lang.reflect.*;

public class Generics {
public BeanFactory<?> createFactory(
String beanClassName
) throws
NullPointerException,
LinkageError,
ExceptionInInitializerError,
ClassNotFoundException,
ClassCastException,
NoSuchMethodException,
SecurityException,
InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Class<?> rawClass = Class.forName(beanClass);
Class<? extends BeanFactory> clazz =
rawClass.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> ctor =
clazz.getConstructor(new Class[] { });
BeanFactory erased = ctor.newInstance(new Object[] { });
BeanFactory<?> reified = erased;
return reified;
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}
I must use reflection to instantiate the factory. Otherwise, I would
not be posting that question ;)

Why must?

What I mean was to create something like:

class BeanFactoryFactory {
public BeanFactory<?> createFactory();
}

And Generics.createFactory would become:

public BeanFactory<?> createFactory(String beanClassName) {
BeanFactoryFactory factoryFactory = ... beanClassName ...
return factoryFactory.createFactory();
}

Tom Hawtin
 
M

Michel T.

Rather strangely it requires an intermediate variable. I have no idea
why. Captures are odd.
I tried and you are right, it got rid of the warning. But doing so, I
had changed the signature of the method that broke the client code
elsewhere. So I am back to the original version of the method that has
this signature:

BeanFactory said:
Why must?

Here is a more complete example that explains in the comments why I am
doing all of this. If you enjoy generics enough to take a look at it
and comment, I'll be more than impressed!

import java.lang.reflect.Constructor;

public class Generics {

/**
* this code will be used in either an OSGi container,
* or in a normal java application.
* So Service implementations are loaded either from
* the OSGi bundle context (when in an OSGi container)
* or from the current class loader using reflection.
* The service finder interface allows to locate the
* Service implementation using a different strategy depending
* on the runtime context.
* Only the reflection strategy is shown here.
* Experimenting with it, I found that I would
* not have to define generices in the interfaces and class
* if I did not have this method:
*
* public void doSomethingWith(T component);
*
* in the Service interface. Wanting to allow a generic type
* as a parameter appears to force me to put generics everywhere.
*/
public static interface ServiceFinder<T extends ServiceComponent> {
public Service<ServiceComponent> find(String id) throws Exception;
}

public static class ServiceFinderUsingReflection
implements ServiceFinder<ServiceComponent> {

public Service<ServiceComponent> find(String serviceId)
throws Exception{

// in real life, the service class name
// is computed differently using
// a pattern or xml config file
String serviceClass = serviceId;

Class<?> bc = Class.forName(serviceClass);
Class<? extends Service> bfc = bc.asSubclass(Service.class);

Constructor<? extends Service> cstr =
bfc.getConstructor(new Class[]{});
Service<?> ret = cstr.newInstance(new Object[]{});

// This is the warning I'd like to get rid of
return (Service<ServiceComponent>)ret;
}
}

public static interface Service<T extends ServiceComponent>{

public T createServiceComponent();

/**
* Using generics here allows implementation class to
* override using their own types.
*/
public void doSomethingWith(T component);
}

public static abstract class ServiceComponent{
}


// ------------------------------------
// Sub-system external implementation
// of Bean and BeanFactory

public static class ServiceComponentX extends ServiceComponent {
public void doIt(){
}
}

public static class ServiceX implements Service<ServiceComponentX>{

/**
* Here, using generics, I can define methods that expect
* a ServiceComponentX.
*/
public void doSomethingWith(ServiceComponentX c) {
c.doIt();
}

public ServiceComponentX createServiceComponent() {
return new ServiceComponentX();
}
}
// ------------------------------------

public static void main(String[] args) throws Exception {


// Use the reflection finder. We may also have chosen to
// use, say, an OSGi-based finder that locates the service
// using the OSGi bundle context.
ServiceFinder<ServiceComponent> finder =
new ServiceFinderUsingReflection();

// Locate the service identified by the service id "ServiceX".
// In this example, it happens to be the class name but in real
// life, it is not the class name, but an ID from which the finder
// implementation uses to locate the real service.
Service<ServiceComponent> service = finder.find("ServiceX");

ServiceComponent component = service.createServiceComponent();
// Setup the component state here
// ...

// once it is setup,
service.doSomethingWith(component);
}
}
 
T

Tom Hawtin

Michel said:
I tried and you are right, it got rid of the warning. But doing so, I
had changed the signature of the method that broke the client code
elsewhere. So I am back to the original version of the method that has
this signature:

BeanFactory<Bean> createFactory2(Strig name)

You have not and in general cannot verify that the object is of that
type. The signature is wrong. For instance, in your example ServiceX is
not a Service<ServiceComponent> but it is a Service<? extends
ServiceComponent>.

Generally a good move when using reflection and generics, is to add a
layer of indirection, so you don't have to mix the two.

So instead of loading Service<? extends ServiceComponent>, load a
ServiceProvider:

interface ServiceProvider {
<T extends ServiceComponent> Service<T> find(
Class<T> componentClass
);
}

class ServiceProviderX implements ServiceProvider {
@SuppressWarnings("unchecked")
<T extends ServiceComponent> Service<T> find(
Class<T> componentClass
) {
if (componentClass != ServiceComponentX.class) {
throw new SomeException();
}
return (Service<T>)new ServiceX();
}
}

I think you are stuck with the unchecked warning there, but at least the
code is now sound. Alternatively, add an asSubclass-like method to Service.

Really, we want a Service interface for each type of ServiceComponent. So:

public interface ServiceComponent {
}
public interface Service<T extends ServiceComponent> {
T createServiceComponent();
void doSomethingWith(T component);
}
public interface XServiceComponent implements ServiceComponent {
}
public interface XService extends Service<XServiceComponent> {
}
class XServiceComponentImpl implements XServiceComponent {
}
public class XServiceImpl implements XService {
public XServiceComponent createServiceComponent() {
return new XServiceComponentImpl();
}
public void doSomethingWith(XServiceComponent component) {
...
}
}
....
public <T extends Service<?>> find(
Class<T> clazz, String serviceClassName
) throws
NullPointerException,
LinkageError,
ExceptionInInitializerError,
ClassNotFoundException,
ClassCastException,
NoSuchMethodException,
SecurityException,
InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
...
return clazz.cast(service);
}
....
// Or: ServiceX service = find(
Service<XServiceComponent> service = find(
XService.class, className
);

Tom Hawtin
 
M

Michel T.

Tim, thank you very much for the feedback.
You have not and in general cannot verify that the object is of that
type. The signature is wrong. For instance, in your example ServiceX is
not a Service<ServiceComponent> but it is a Service<? extends
ServiceComponent>.

But since ServiceComponent is an abstract class or interface, in
practice, is'nt Service<? extends ServiceComponent> a "subset" of
Generally a good move when using reflection and generics, is to add a
layer of indirection, so you don't have to mix the two.

On the other hand, it seems more work for the service implementation
(ServiceX) who would have to provide two interfaces, and matching
implementation classes. I may be missing a point though.

I updated my test class to follow some of your recommandation. this is
whet I end up with.

import java.lang.reflect.Constructor;

public class Generics2 {

static interface ServiceProvider {
<T extends ServiceComponent> Service<T> find(
String className)
throws Exception;
}

static class ServiceProviderWithReflection implements ServiceProvider
{
@SuppressWarnings("unchecked")
public <T extends ServiceComponent> Service<T> find(
String className) throws Exception {
Class<?> bc = Class.forName(className);
Class<? extends Service> bfc = bc.asSubclass(Service.class);

Constructor<? extends Service> cstr =
bfc.getConstructor(new Class[]{});
Service<?> ret = cstr.newInstance(new Object[]{});

// It seems we will have to live with this warning
return (Service<T>)ret;
}
}

static public interface ServiceComponent {
}

static public interface Service<T extends ServiceComponent> {
T createServiceComponent();
void doSomethingWith(T component);
}

static class ServiceComponentX implements ServiceComponent {
}

static public class ServiceX implements Service<ServiceComponentX> {
public ServiceComponentX createServiceComponent() {
return new ServiceComponentX();
}
public void doSomethingWith(ServiceComponentX component) {

}
}
public static void main(String[] args) throws Exception {


// Use the reflection finder. We may also have chosen to
// use, say, an OSGi-based finder that locates the service
// using the OSGi bundle context.
ServiceProvider finder = new ServiceProviderWithReflection();

// Locate the service identified by the service id "ServiceX".
// In this example, it happens to be the class name but in real
// life, it is not the class name, but an ID from which the finder
// implementation uses to locate the real service.
Service<ServiceComponent> service =
finder.find(Generics2.ServiceX.class.getName());

ServiceComponent component = service.createServiceComponent();
// Setup the component state here
// ...

// once it is setup,
service.doSomethingWith(component);
}
}
 
T

Tom Hawtin

Michel said:
Tim, thank you very much for the feedback.


But since ServiceComponent is an abstract class or interface, in
practice, is'nt Service<? extends ServiceComponent> a "subset" of
Service<ServiceComponent> ? Therefore, is'nt the cast from the later
into the former safe?

No. The set of all objects that are of type Service<? extends
ServiceComponent> is a superset of all objects that are of type
Service<ServiceComponent> (gloss over erasure here...).

You had an interface:

public interface Service<T extends ServiceComponent> {
T createServiceComponent();
void doSomethingWith(T component);
}

Suppose ServiceComponentX extends ServiceComponent. Then
Service<ServiceComponentX> is a Service<? extends ServiceComponent>. Now
if we could assign that to Service<ServiceComponent> we have a problem.


Service<ServiceComponentX> a = new Service<ServiceComponentX>() {
public ServiceComponentX createServiceComponent() {
return new ServiceComponentX();
}
public void doSomethingWith(ServiceComponentX component) {
// We hope this is true.
System.err.println(component instanceof ServiceComponentX);
}
};
Service<? extends ServiceComponent> b = a;
Service<ServiceComponentX> c = b; // wrong

// Here ServiceComponent is implicitly casted to ServiceComponentX!
c.doSomething(new ServiceComponent());
On the other hand, it seems more work for the service implementation
(ServiceX) who would have to provide two interfaces, and matching
implementation classes. I may be missing a point though.

Two interfaces containing no definitions.

They are matching the types used by the client code. You could have more
than one service per type (otherwise what would the point of dynamically
loading them be?).

Tom Hawtin
 
M

Michel T.

Tom, thanks a lot. Your comments and suggestions have been extremely
useful to me.
 
J

John Ersatznom

Michel said:
But since ServiceComponent is an abstract class or interface, in
practice, is'nt Service<? extends ServiceComponent> a "subset" of
Service<ServiceComponent> ? Therefore, is'nt the cast from the later
into the former safe?

This is a surprisingly common generics question (given it's answered
darn near everywhere and every week here), and the answer, incidentally,
is "no".

Foo<X> and Foo<? extends X> are not subtypes of each other, because of
example cases like this:

List<Object> lo = new ArrayList<Object>();
lo.add("foo"); // OK, a String is an Object.
List<Integer> li = lo; // Fortunately won't work.
Integer i = li.iterator().next(); // Uh-oh.

List<Integer> li = new ArrayList<Integer>();
List<Object> lo = li; // Fortunately won't work.
lo.add("foo");
Integer i = li.iterator().next(); // Uh-oh.

Notice that either case allows us to wind up pulling a String from a
List<Integer>! If we can get the same list referenced as a List<Object>
and a List<Integer> we can put a non-Integer in via the former and pull
out a nice fat ClassCastException via the latter. So much for
compile-time type safety.

Of course, a List<Integer> is a List<? extends Object>. And a
List<Object> is a List<? super Integer>.

The type safety rules basically don't let us put anything into a List<?
extends Object> but we can read out Objects, and don't let us retrieve
from a List<? super Integer> but we can put things in. Neither List
sounds useful, but if we have a List<Integer> and pass it somewhere
expecting a List<? extends Object> the latter can read our integers, and
we can still put things in via the reference of type List<Integer>. The
latter doesn't know they are integers, mind you, but it can count them,
invoke all their toString methods, or whatever. Likewise if we pass a
List<Object> to something expecting a List<? super Integer> that
something can dump Integers into the list (and it can actually read
Objects out, since whatever's in the list has to extend Object, but
anything more requires an explicit cast).

This comes in useful once you write generic stuff. Consider a game with
a GameObject class. The instances are all things like game enemies,
powerups, and even the player that can be drawn on the screen and can
potentially move around and stuff. The GameObject class is polymorphic,
and subclasses like Powerup don't do anything in their move() methods
while others like Enemy use AI and Player uses the input device to
decide where to move. (The input device might even be abstracted as a
Connection, and you've got no problem later adding online multiplayer!
Just add a NetworkConnection to the existing ConsoleConnection as
concrete Connection types.) Now we want some generic display functions
that display GameObjects from a List. One displays them in the main area
at their proper positions; another displays them at particular
coordinates in a grid in a corner area to show the graphics for
currently active powerups.

The first takes a List<GameObject> and is basically
for (GameObject go : list) { go.display(go.getX(), go.getY() }

The second takes a Set<Powerup>, since that's what our powerup state is,
with objects added as they are acquired and removed when they run out.
It does
x = 0; y = 0; for (Powerup p : list) { p.display(x, y); /* some logic to
move x and y*/ }

Now we want to add that networked multiplayer and show the players'
graphics in a scoreboard list from time to time. Why create a new
function for this when players form a Set and we already have a
Set<Powerup> displaying method? Let's change it to accept a Set<GameObject>.

Oops. Ah yes, that's why we had it a Set<Powerup> to begin with; we
can't assign our Powerup set to a Set<GameObject>. Or our Set<Player>
either for that matter.

But it's perfectly safe; the loop doesn't add anything to the Set after
all! That's why we want:

public void drawInGridAt
(int x, int y, int size, Set<? extends GameObject> objects) {
// some logic setting i and j up
for (GameObject go : objects) {
go.display(x + size*i, y + size*j);
// some logic changing i and j
}
}

Presto: we can pass a Set<Powerup> or a Set<Player> as a Set<? extends
GameObject> and we can pull a GameObject from a Set<? extends
GameObject>, which is all this method needs to do.


Of course, there's a nice example for <? super Foo> as well. It's when
we are inputting objects of a generic type but not retrieving them.
Consider a pipeline of some kind in which objects are put into a
Pipeline object and Pipelines notify their Listeners with these objects,
allowing loose coupling and runtime rearrangement of the plumbing. This
might be for process coordination or whatever. Ultimately, we need a
Pipeline<T> to accept Ts and pass them to Listener<T>s. We also want to
be able to join pipelines together so that many run together; if we do a
pipelineA.mergeInto(pipelineB) we want any object sent to pipeline A to
get sent to B as well, and on to anything B does in turn. We come up with:

public interface Listener<T> {
public void handleObject(T object);
}

public class Pipeline<T> {
private List<Listener<T>> listeners
= new List<Listener<T>>();

public void handleObject(T object) {
for (Listener<T> listener : listeners) {
listener.handleObject(object);
}
}

public void addListener (Listener<T> listener) {
listeners.add(listener);
}

public void mergeInto (Pipeline<T> p) {
// hmm ... how to do this?
}
}

Then the brilliant idea strikes: just add "implements Listener<T>" to
the class definition. Pipeline already has a handleObject(T) method, and
that method does exactly what's needed to pass the object on to its own
listeners, so adding a Pipeline as a listener to another Pipeline works
beautifully. And mergeInto just has to behave like addListener(p)! In
fact, the method could go away entirely, save that it helps callers
write self-documenting code that makes it clear that major plumbing is
merging and not just being tapped at that point.

Unfortunately, we run into a problem when we go to merge a
pipeline<Integer> to a pipeline<Object>. Logically we should be able to,
since any listener that can cope with an Object can cope with an
Integer, and a flow of Integers dumping into a flow of Objects isn't
violating any type constraints, surely?

It's "super" to the rescue:

public class Pipeline<T> implements Listener<T> {
private List<Listener<? super T>> listeners
= new List<Listener<? super T>>();

public void handleObject(T object) {
for (Listener<? super T> listener : listeners) {
listener.handleObject(object);
// Fine -- object is a T so it can safely
// be cast to any ? super T
}
}

public void addListener (Listener<? super T> listener) {
listeners.add(listener);
}

public void mergeInto (Pipeline<? super T> p) {
listeners.add(p);
}
}

This also lets us register a generic Object listener with an Integer
pipeline, as well as merging pipelines of Integers, Strings, and
whatever else into a pipeline of Objects.
 
M

Michel T.

This is a very useful reply to my question. Thank you very much for
taling the time to write it.
 
J

John Ersatznom

Michel said:
This is a very useful reply to my question. Thank you very much for
taling the time to write it.

What is? You don't quote it and it doesn't seem to exist on my
newsserver any more.
 

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,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top