Is there a way to declare a reference as being a type implementing multiple interfaces?

O

Oliver Wong

Let's say I have these two interfaces and these two classes:

<code>
public interface IOne {
public void methodOne();
}

public interface ITwo {
public void methodTwo();
}

public class A implements IOne, ITwo {
/*Implementation of the methods here*/
}

public class B implements IOne, ITwo {
/*Implementation of the methods here*/
}
</code>

And now I have code, for which either A or B would do the job fine, or even
some as of yet unknown class C, as long as it implements the IOne and ITwo
interfaces.

That is, I'd like to declare a reference as being of type IOne and ITwo at
the same time: something like:

<code>
private int myCode(IOne ITwo myVar, int someParam) {
myVar.methodOne();
myVar.methodTwo();
return someParam;
}
</code>

Assume that classes A and B come from third parties, so I can't just invent
a new interface IOneAndTwo, and have A and B implement those. All the
solutions I can come up with are a bit messy.

<code>
private int myCode(IOne myVar, int someParam) {
if (!(myVar instanceof ITwo)) {
throw new IllegalArgumentException("myVar has to implement ITwo");
}
myVar.methodOne();
((ITwo)myVar).methodTwo();
return someParam;
}
</code>

<code>
private int myCode(IOne myVar1, ITwo myVar2 int someParam) {
if (myVar1 != myVar2) {
throw new IllegalArgumentException("myVar1 and myVar2 must refer to the
same object");
}
myVar1.methodOne();
myVar2.methodTwo();
return someParam;
}
</code>

etc.

Any ideas on how to declare (or simulate) a multiple-interface-type
reference?

- Oliver
 
R

Robert Klemme

Oliver said:
Let's say I have these two interfaces and these two classes:

<code>
public interface IOne {
public void methodOne();
}

public interface ITwo {
public void methodTwo();
}

public class A implements IOne, ITwo {
/*Implementation of the methods here*/
}

public class B implements IOne, ITwo {
/*Implementation of the methods here*/
}
</code>

And now I have code, for which either A or B would do the job fine, or
even some as of yet unknown class C, as long as it implements the IOne
and ITwo interfaces.

That is, I'd like to declare a reference as being of type IOne and ITwo
at the same time: something like:

<code>
private int myCode(IOne ITwo myVar, int someParam) {
myVar.methodOne();
myVar.methodTwo();
return someParam;
}
</code>

Assume that classes A and B come from third parties, so I can't just
invent a new interface IOneAndTwo, and have A and B implement those. All
the solutions I can come up with are a bit messy.

<code>
private int myCode(IOne myVar, int someParam) {
if (!(myVar instanceof ITwo)) {
throw new IllegalArgumentException("myVar has to implement ITwo");
}

You don't need that exception as the client will be bitten by a
ClassCastException anyway if he passes an instance that doesn't
implement the other interface.
myVar.methodOne();
((ITwo)myVar).methodTwo();
return someParam;
}
</code>

<code>
private int myCode(IOne myVar1, ITwo myVar2 int someParam) {
if (myVar1 != myVar2) {
throw new IllegalArgumentException("myVar1 and myVar2 must refer to
the same object");
}
myVar1.methodOne();
myVar2.methodTwo();
return someParam;
}
</code>

That looks even more awkward that the first one.
Any ideas on how to declare (or simulate) a multiple-interface-type
reference?

If these interfaces are so closely related one should probably inherit
the other one. Other than that I don't know

Kind regards

robert
 
O

Oliver Wong

Robert Klemme said:
If these interfaces are so closely related one should probably inherit the
other one. Other than that I don't know

Right, I forgot to mention I can't edit the interfaces either.
Specifically, I'm working with the Eclipses interfaces pentuplet: IDocument,
IDocumentExtension, IDocumentExtension2, IDocumentExtension3 and
IDocumentExtension4. As long as the object getting passed in implements all
five of those interfaces, my code will work. Now I'd like to specify the
requirement that the object implements all five of those interfaces in the
method signature somehow.

I agree with you about the interfaces should be inheritting each other.
I've submitted that as a RFE to the Eclipse team, but until they make the
changes and make an official release of their code, I gotta find a
workaround.

- Oliver
 
P

Patricia Shanahan

Robert said:
Oliver Wong wrote: ....

You don't need that exception as the client will be bitten by a
ClassCastException anyway if he passes an instance that doesn't
implement the other interface.

An IllegalArgumentException, especially with a message naming the
argument and saying what test it failed, may be easier to document and
interpret than a ClassCastException.

Patricia
 
C

Chris Smith

Oliver Wong said:
That is, I'd like to declare a reference as being of type IOne and ITwo at
the same time: something like:

<code>
private int myCode(IOne ITwo myVar, int someParam) {
myVar.methodOne();
myVar.methodTwo();
return someParam;
}
</code>

Nope, sorry. There are no intersection types in Java. I'd just pick
one and check the other with instanceof. If it fits your aesthetic
sense better (neither interface is "more" important), you might even
eskew types here entirely and ask for an Object, which would make it
more obvious that the client should check the documentation for a
description of what kind of object to pass.

If you find yourself doing this a lot, then something like the following
might come in handy:

class OneTwoWrapper implements IOne, ITwo
{
private IOne one;
private ITwo two;

public OneTwoWrapper(Object obj)
{
one = (IOne) obj;
two = (ITwo) obj;
}

... implement forwarding methods ...
}
 
S

Stefan Ram

Oliver Wong said:
That is, I'd like to declare a reference as being of type IOne and ITwo at
the same time: something like:

public class Main
{
static
< T extends java.lang.Object &
java.io.Serializable &
java.lang.Comparable< T >>
void f( final T t ){}

public static void main( final java.lang.String[] args )
{ f( new java.lang.String( "0" ));
f( new java.lang.Long( 0 )); }}
 
S

Stefan Ram

Oliver Wong said:
private int myCode(IOne ITwo myVar, int someParam) {

interface IOne { void methodOne(); }
interface ITwo { void methodTwo(); }

class A implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

class B implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

public class Main
{
private
< T extends IOne & ITwo >
int myCode( final T myVar, int someParam )
{ myVar.methodOne();
myVar.methodTwo();
return someParam; }

public static void main( final java.lang.String[] args )
{ new Main().myCode( new A(), 0 ); new Main().myCode( new B(), 0 ); }}
 
O

Oliver Wong

Oliver Wong said:
I agree with you about the interfaces should be inheritting each other.
I've submitted that as a RFE to the Eclipse team, but until they make the
changes and make an official release of their code, I gotta find a
workaround.

I submitted the bug, but they've responded with "there is no chance that
we address this.": https://bugs.eclipse.org/bugs/show_bug.cgi?id=148660

I guess I'll have to stick with Stefans fix.

- Oliver
 
O

Oliver Wong

Stefan Ram said:
Oliver Wong said:
private int myCode(IOne ITwo myVar, int someParam) {

interface IOne { void methodOne(); }
interface ITwo { void methodTwo(); }

class A implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

class B implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

public class Main
{
private
< T extends IOne & ITwo >
int myCode( final T myVar, int someParam )
{ myVar.methodOne();
myVar.methodTwo();
return someParam; }

public static void main( final java.lang.String[] args )
{ new Main().myCode( new A(), 0 ); new Main().myCode( new B(), 0 ); }}

Thank you very much. This is much better than anything I've come up
with. There's just one minor problem left. It's not a big issue if I can't
resolve it, but if you can figure out a solution, it'll make my code a
little bit more elegant. I have a factory method which uses a static Map for
caching purposes. So the declaration of the field looks like:

<code>
private static final Map<Object, Foo> mapping = new HashMap<Object, Foo>();
</code>

but ideally, I'd like to use your trick and write something like:

<code>
private static final <T extends IOne & ITwo> Map<T, Foo> mapping = new
HashMap<T, Foo>();
</code>

Unfortunately, my compiler complains "Syntax error on tokens, ReferenceType
expected instead", which I guess means the grammar for the Java language
doesn't support this kind of construct. Is there a solution to this one?

- Oliver
 
S

Stefan Ram

Oliver Wong said:
private static final <T extends IOne & ITwo> Map<T, Foo> mapping = new
HashMap<T, Foo>();

My own knowledge about type parameters is very irregular:
I only know about those parts that recently came along my
way incidentally, but I am not an expert in this field.

I was able to compile something similar to the above
code after I removed »static« and moved the declaration
of »T« to the enclosing class:

interface IOne { void methodOne(); }
interface ITwo { void methodTwo(); }

class A implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

class B implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

public class Main< T extends IOne & ITwo >
{
private final java.util.Map<T, java.lang.Object> mapping =
new java.util.HashMap<T, java.lang.Object>();

private
int myCode( final T myVar, int someParam )
{ myVar.methodOne();
myVar.methodTwo();
return someParam; }

public static void main( final java.lang.String[] args )
{ new Main<A>().myCode( new A(), 0 );
new Main<B>().myCode( new B(), 0 );
java.lang.System.out.println( "2006-06-26T19:18:10+02:00" ); }}
 
O

Oliver Wong

Stefan Ram said:
My own knowledge about type parameters is very irregular:
I only know about those parts that recently came along my
way incidentally, but I am not an expert in this field.

I was able to compile something similar to the above
code after I removed »static« and moved the declaration
of »T« to the enclosing class:

[code snipped]

Yeah, I moved T to the enclosing class as well in my code.
Unfortunately, I need the static, because the caching map is being used by a
factory method:

<code>
class Foo<T extends IOne & ITwo> {
private static final Map<Object, Foo> mapping = new HashMap<Object,Foo>();

private Foo(T impl) {
this.impl = impl;
}

private final T impl;

public static <T extends IOne & ITwo> makeFoo(T impl) {
if (!mapping.contains(impl)) {
mapping.put(impl, new Foo(impl));
}
return mapping.get(impl);
}
}
</code>

- Oliver
 
T

Thomas Hawtin

Oliver said:
but ideally, I'd like to use your trick and write something like:

<code>
private static final <T extends IOne & ITwo> Map<T, Foo> mapping = new
HashMap<T, Foo>();
</code>

If you were writing that for one interface, you would have:

private static final Map<? extends Appendable,String> map =
createMap();

private static <T extends Appendable> Map<T,String> createMap() {
return new java.util.HashMap<T,String>();
}

(Implicit capture is a bit more flexible than what you can write
explicitly.)

I've been meaning to have a play with this for intersection types for
some time.

You can slide in a layer of indirection. It does require some copy &
paster work, but it be required too often. Here's some code I wrote a
minute ago:

final class AppendableCharSequenceRef<
T extends Appendable & CharSequence
// The basics...

private final T value;

public AppendableCharSequenceRef(T value) {
this.value = value;
}

public T get() {
return value;
}

// The trimmings...

public void get(Handler<Appendable,CharSequence> handler) {
handler.handle(value, value);
}

public <
N extends Appendable & CharSequence
AppendableCharSequenceRef<?> ref(N value) {
return
this.value==value ?
this :
new AppendableCharSequenceRef<N>(value);
}

public static final AppendableCharSequenceRef<?> NULL =
newInstance(null);

public static <
N extends Appendable & CharSequence
AppendableCharSequenceRef<N> newInstance(
N value
) {
return new AppendableCharSequenceRef<N>(value);
}

public static <
N extends Appendable & CharSequence
AppendableCharSequenceRef<?> of(
N value
) {
return NULL.ref(value);
}
}

interface Handler<A,B> {
void handle(A asA, B asB);
}


class Intersect {
private AppendableCharSequenceRef<?> x;

public Intersect() {
this.x = AppendableCharSequenceRef.NULL;
}

public <T extends Appendable & CharSequence> Intersect(T x) {
this.x = AppendableCharSequenceRef.of(x);
}

public <T extends Appendable & CharSequence> void set(T x) {
this.x = this.x.ref(x);
}

public void set(AppendableCharSequenceRef<?> xRef) {
if (xRef == null) {
throw new NullPointerException();
}
this.x = xRef;
}
public AppendableCharSequenceRef<?> getAsRef() {
return x;
}
public Appendable getAsAppendable() {
return x.get();
}
public CharSequence getAsCharSequence() {
return x.get();
}

public void get(Handler<Appendable,CharSequence> handler) {
x.get(handler);
}

public void append(char c) throws java.io.IOException {
x.get().append(c);
}
public char getAt(int off) {
return x.get().charAt(off);
}
}

Tom Hawtin
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Stefan Ram schreef:
interface IOne { void methodOne(); }
interface ITwo { void methodTwo(); }

class A implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

class B implements IOne, ITwo
{ public void methodOne(){} public void methodTwo(){} }

public class Main
{
private
< T extends IOne & ITwo >
int myCode( final T myVar, int someParam )
{ myVar.methodOne();
myVar.methodTwo();
return someParam; }

Do note that, if you use this with 1.4 code, the erasure is
private int myCode(final IOne myVar, int someParam);
so interoperating might be difficult (solution: put another Object &
between extends and IOne).

If I understood right, the reason why these intersection types where
introduced was to provide backwards compatibility by erasure, but this
is a nice application of it, which probably was unforeseen.

H.
- --
Hendrik Maryns

==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)

iD8DBQFEoOjUe+7xMGD3itQRArSYAJ4n9RMjFqiFlPc7zePUNq0iJM7uOgCfRcJQ
Cwdom/pgZOEwb12IIgF6p/g=
=q4l5
-----END PGP SIGNATURE-----
 
O

Oliver Wong

Furious George said:
I don't see this as a problem. By encapsulation, you should not worry
too much about the internal state of objects.

Well, what if the interfaces were like:

<code>
public interface IOne {
/**
* Modifies something. Notifies all listeners of the changes.
*/
public void modifySomething();
}

public interface ITwo {
/**
* Prevents notification from occurring. Useful for batch modifications,
* so that all listeners are notified after all the changes are made,
instead
* of after each change. Caller is responsible for calling
* resumeNotifications().
*/
public void temporarilyStopListenerNotification();

public void resumeNotifications();
}
</code>

This is sort of the situation I have with Eclipse' IDocument, and
IDocumentExtension interfaces.
But if you insist on
worrying, you can specify the Constructors in OneAndTwo to ensure o and
t are the same object like this:

private OneAndTwo ( ) { throw ( new IllegalArgumentException ( ) ) ; }
// no no-argument constructor allowed
private OneAndTwo ( IOne o , ITwo t ) { o=o ; t=t ; }
public OneAndTwo ( A obj ) { this ( obj , obj ) ; } // o and t are the
same
public OneAndTwo ( B obj ) { this ( obj , obj ) ; } // B also
implements IOne,ITwo but not OneAndTwo

But now I'd have to enumerate all classes that actually implement IOne
and ITwo, which may not be possible. You've got A and B here, but what about
the unknown class C which may be developed by some third party in the
future?

- Oliver
 
F

Furious George

Oliver said:
Well, what if the interfaces were like:

<code>
public interface IOne {
/**
* Modifies something. Notifies all listeners of the changes.
*/
public void modifySomething();
}

public interface ITwo {
/**
* Prevents notification from occurring. Useful for batch modifications,
* so that all listeners are notified after all the changes are made,
instead
* of after each change. Caller is responsible for calling
* resumeNotifications().
*/
public void temporarilyStopListenerNotification();

public void resumeNotifications();
}
</code>

This is sort of the situation I have with Eclipse' IDocument, and
IDocumentExtension interfaces.

Just because o and t refer to the same object, does not mean that the
object was properly implemented.
<source code>
public class BadImp implements IOne, ITwo {
public void modifySomething ( ) { ... } // do modifications and notify
all listeners
public void temporarilyStopListenerNotification { ... } // send garbage
notification to some subscribers
public void resumeNotifications ( ) { ... } // do nothing
</source code>

OTH o and t could refer to different objects, yet be properly
implemented
</source code>
public class GoodImplOne implements IOne {
GoodImplOne ( GoodImplTwo two ) { two=two ; }
public void modifySomething ( ) { doModification ( ) ; two.notify ( ) ;
}
private void doModification ( ) { ... } // do modifications
}
public class GoodImplTwo implements ITwo {
public void notifiy ( ) { ... } notify all listeners now or when
notifications has been resumed
public void temporarilyStopListenerNotification { ... }
public void resumeNotifications ( ) { ... }
}
But now I'd have to enumerate all classes that actually implement IOne
and ITwo, which may not be possible. You've got A and B here, but what about
the unknown class C which may be developed by some third party in the
future?

In your original post, all your methods were private. It should be
easy to enumerate all the classes that actually implement IOne and ITwo
that you actually use. If you do not use class C in your code, why
should you care about it? The OneAndTwo class may not be terribly
useful to other people (who use class C), but who cares. They can
write their own OneAndTwo class.

OTH, if you are writing methods that will be public (and thus used by
other people) then this could be a problem. But I still maintain it is
not necessary to insure that o and t are the same object.
 
O

Oliver Wong

Furious George said:
In your original post, all your methods were private. It should be
easy to enumerate all the classes that actually implement IOne and ITwo
that you actually use. If you do not use class C in your code, why
should you care about it?

I don't actually "use" class A or B either, in my code. I only use the
IOne and ITwo interface, and whatever gets passed in gets used. To use
specific class names from my actual problem, I don't actually use the
org.eclipse.jface.text.AbstractDocument; instead, I use the IDocument and
IDocumentExtension interfaces. The JavaDocs for IDocument specifically say
that clients are free to implement the interface with new classes.

The idea is that I write my code to work with certain interfaces, and if
the user installs new plugins into Eclipse that add new implementations of
IDocument and IDocumentExtension (that mysterious class C), my code will
work with those new plugins without any change. The Eclipse platform itself
will take care of passing the instance of C to my code.
The OneAndTwo class may not be terribly
useful to other people (who use class C), but who cares. They can
write their own OneAndTwo class.

They might never know about the OneAndTwo classes, particularly if
they're just writing some random Eclipse plugin with no knowledge of my code
at all. They'll read the Eclipse plugin API documentation, and only see the
two interfaces. So they'll make a class which implements those two
interfaces, and it should work with anything else that's hooked up into
Eclipse, including my code.
OTH, if you are writing methods that will be public (and thus used by
other people) then this could be a problem. But I still maintain it is
not necessary to insure that o and t are the same object.

My original requirements is having a SINGLE objected passed in which
implements two interfaces. Since the Java language doesn't directly support
that[*], I'm "faking it" by having the method signature take two references.
Conceptually, these two references should represent the same object, so that
any state changes done to one will be reflected in the other.

As you said (in the comment that I snipped), there's always the
possibility that whoever implements the interface correctly. But if that
occurs, I can pass the buck, saying it's their code that's broken, not mine.
I only have to worry about objects which respect their contracts, and make
sure that my code fufills my end of the deal.

- Oliver

[*]: Except see Hendrik's solution posted elsewhere in this thread, which
*almost* solves it.
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top