Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / June 2006

Tip: Looking for answers? Try searching our database.

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

Thread view: 
Oliver Wong - 26 Jun 2006 16:06 GMT
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
Robert Klemme - 26 Jun 2006 16:25 GMT
> Let's say I have these two interfaces and these two classes:
>
[quoted text clipped - 40 lines]
>    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();
[quoted text clipped - 13 lines]
> }
> </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
Oliver Wong - 26 Jun 2006 16:30 GMT
>>    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

   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
Oliver Wong - 26 Jun 2006 17:32 GMT
>> If these interfaces are so closely related one should probably inherit
>> the other one.  Other than that I don't know
[quoted text clipped - 3 lines]
> 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
Patricia Shanahan - 26 Jun 2006 16:38 GMT
...
>> 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.
[quoted text clipped - 9 lines]
> 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
Chris Smith - 26 Jun 2006 16:43 GMT
> That is, I'd like to declare a reference as being of type IOne and ITwo at
> the same time: something like:
[quoted text clipped - 6 lines]
> }
> </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 ...
   }

Signature

Chris Smith - Lead Software Developer / Technical Trainer
MindIQ Corporation

Stefan Ram - 26 Jun 2006 16:47 GMT
>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 )); }}
Chris Smith - 26 Jun 2006 16:49 GMT
>   static
>   < T extends java.lang.Object &
>     java.io.Serializable &
>     java.lang.Comparable< T >>
>   void f( final T t ){}

Oh, wow!  You're right.

Signature

Chris Smith - Lead Software Developer / Technical Trainer
MindIQ Corporation

Stefan Ram - 26 Jun 2006 16:57 GMT
>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 ); }}
Oliver Wong - 26 Jun 2006 17:49 GMT
>>private int myCode(IOne ITwo myVar, int someParam) {
>
[quoted text clipped - 18 lines]
>  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
Stefan Ram - 26 Jun 2006 18:23 GMT
>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" ); }}

 
Oliver Wong - 26 Jun 2006 18:44 GMT
>>private static final <T extends IOne & ITwo> Map<T, Foo> mapping = new
>>HashMap<T, Foo>();
[quoted text clipped - 6 lines]
>  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
Thomas Hawtin - 26 Jun 2006 19:16 GMT
>    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:

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

>  private Foo(T impl) {
>     this.impl = impl;
[quoted text clipped - 5 lines]
>    if (!mapping.contains(impl)) {
>      mapping.put(impl, new Foo(impl));
                                <T>
>    }
>    return mapping.get(impl);
>  }
> }

Won't you still have problems with a single base type? It seems as if
you have a heterogeneous map. The usual cure for that, is to add
something like Class.cast to the key.

Being static you should also make it thread-safe. A ConcurrentHashMap
would fit nicely.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Oliver Wong - 26 Jun 2006 19:56 GMT
>>    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
[quoted text clipped - 23 lines]
> have a heterogeneous map. The usual cure for that, is to add something
> like Class.cast to the key.

   The Map isn't heterogeneous in the sense that all keys are known to
implement IOne and ITwo simultaneously. So the type of the key would be
something like <? extends IOne & ITwo> (if only the compiler accepted that).

> Being static you should also make it thread-safe. A ConcurrentHashMap
> would fit nicely.

   Thanks, I'll fix that.

   - Oliver
Thomas Hawtin - 26 Jun 2006 18:50 GMT
> 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
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Hendrik Maryns - 27 Jun 2006 09:14 GMT
Stefan Ram schreef:
>> private int myCode(IOne ITwo myVar, int someParam) {
>
[quoted text clipped - 15 lines]
>     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
a_szczeblewski@poczta.onet.pl - 26 Jun 2006 19:18 GMT
> Let's say I have these two interfaces and these two classes:
>
[quoted text clipped - 10 lines]
>   /*Implementation of the methods here*/
> }

Can you do that?:

public interface IOneTwo extends IOne, ITwo {}

public class A implements IOneTwo

void method(IOneTwo ref)
{
ref.methodOne();
ref.methodTwo();
}
Oliver Wong - 26 Jun 2006 19:54 GMT
>> Let's say I have these two interfaces and these two classes:
>>
[quoted text clipped - 22 lines]
> ref.methodTwo();
> }

   No, I can't change the classes or the interfaces, as they're provided by
a 3rd party.

   - Oliver
Andrew McDonagh - 26 Jun 2006 19:50 GMT
> Let's say I have these two interfaces and these two classes:
>
[quoted text clipped - 22 lines]
> That is, I'd like to declare a reference as being of type IOne and ITwo
> at the same time: something like:

interface IOneAndITwo extends IOne, ITwo {
}

> <code>
> private int myCode(IOne ITwo myVar, int someParam) {
[quoted text clipped - 7 lines]
> invent a new interface IOneAndTwo, and have A and B implement those. All
> the solutions I can come up with are a bit messy.

snipped

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

Ok, so you cant change the classes to implement the new interface, you
can create a new class that implements it and delegates to the 3rd party
class's objects.....

class OneAndTwo implements IOneAndITwo {

    One o = new com.othercompany.One();
    Two t = new com.othercompany.Two();

    public void methodOne() {
       o.methodOne();
    }

    public int method2() {
       return t.methodTwo();
    }
}

>    - Oliver
Oliver Wong - 26 Jun 2006 19:53 GMT
> >    Any ideas on how to declare (or simulate) a multiple-interface-type
> > reference?
[quoted text clipped - 16 lines]
>     }
> }

   This sort of just moves the problem around, instead of solving it: I'd
still need to ensure o and t refer to the same object (in case methodOne
affects the state which would be visible in methodTwo).

   - Oliver
Furious George - 27 Jun 2006 01:38 GMT
> > >    Any ideas on how to declare (or simulate) a multiple-interface-type
> > > reference?
[quoted text clipped - 20 lines]
> still need to ensure o and t refer to the same object (in case methodOne
> affects the state which would be visible in methodTwo).

I don't see this as a problem.  By encapsulation, you should not worry
too much about the internal state of objects.  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

>     - Oliver
Oliver Wong - 28 Jun 2006 15:10 GMT
>>     This sort of just moves the problem around, instead of solving it:
>> I'd
[quoted text clipped - 3 lines]
> 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
[quoted text clipped - 7 lines]
> 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
Furious George - 28 Jun 2006 17:32 GMT
> >>     This sort of just moves the problem around, instead of solving it:
> >> I'd
[quoted text clipped - 30 lines]
>     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 ( ) { ... }
}
</source code>

> >  But if you insist on
> > worrying, you can specify the Constructors in OneAndTwo to ensure o and
[quoted text clipped - 12 lines]
> 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.

>     - Oliver
Oliver Wong - 28 Jun 2006 18:22 GMT
>> "Furious George" <bugme_69@hotmail.com> wrote in message

>> >  But if you insist on
>> > worrying, you can specify the Constructors in OneAndTwo to ensure o and
[quoted text clipped - 19 lines]
> 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.


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.