Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Requiring an argument extends a particular class AND implements a particular interface

I have two Java class hierarchies that share a common ancestor and implement a common interface. I need to pass a pointer to one of these things to a method in another class.

interface I { ... }

class A extends java.awt.Component implements I { ... }
class B extends java.awt.Component implements I { ... }

class D {
  Component c;
  I i;
  void m(? x) {
    c = (Component) x;
    i = (I) x;
  }
}

Is there something I can replace the '?' with that will allow me pass in either an 'A' or a 'B'? If I cast 'x' to a java.awt.Component and store it in 'c' and to an I and store it in 'i', I lose the benefit of strong typing.

Do I need to declare

class D {
  void m(java.awt.Component c, I i) { ... }
}

and call it with 'm(a, a)' or 'm(b, b)' where

A a = new A();
B b = new B();

I cannot create an

abstract class C extends java.awt.Component implements I {...}

and pass that in because neither A nor B is a C.

BTW, can this be done in Scala?

EDIT: The actual problem that I am trying to solve is that I have two classes, one that extends JInternalFrame and another that extends JPanel. Both are abstract and provide some common functionality for widgets displayed in them (JTables where the user can edit rows, delete rows, etc). The code for editing and deleting rows is always the same, regardless of the underlying object types being displayed. I have several methods that allow the user to click a row, select 'Delete' from a popup menu, and, after asking for confirmation, deletes the selected row and database object, for example. Sometimes I need a frame subcomponent and at other times a panel subcomponent. I have created a delegate class for the common functionality and an instance variable in each of the abstract classes of that delegate type. The JInternalFrame and JPanel derived classes then just defer the actual implementations to the delegate. The delegate class, in turn, needs a pointer to the "owner" class for callbacks to get the selected table row, etc., and a pointer to the "Component" nature of each parent for the JOptionPane confirmation dialogs.

Using the Java generics approach has worked very well. The delegate class is now defined as a generic class on <T extends Component & DelegateContainer and each of the owner abstract classes implements DelegateContainer (where the callback methods are declared).

If I were going to rewrite this in Scala, I would probably do something with traits instead of creating a delegate class. The traits could "add" the delete functionality to the JInternalFrame derived concrete class, for example.

Thanks for the prompt replies.

like image 686
Ralph Avatar asked Nov 28 '22 12:11

Ralph


2 Answers

Generics to the rescue !

public class Test {
    public static void main(String... args){
        new D().m(new A());
        new D().m(new B());
    }
}

interface I {  }

class A extends java.awt.Component implements I {}
class B extends java.awt.Component implements I {}

class D {
  Component c;
  I i;
  <T extends java.awt.Component & I> void m(T x) {
    c = x;
    i = x;
  }
}

It's not really the best way to do things but in your case it works. You should really split your method in two, one for the I behavior and another for the Component behavior.

like image 198
Colin Hebert Avatar answered Mar 29 '23 23:03

Colin Hebert


It's ugly, but you can use generics and constrain the method parameter to extend the class and implement the interface. Here's a complete example:

interface Foo {}

class Bar {}

class Impl extends Bar implements Foo {}

class D {

    private Foo foo;
    private Bar bar;

    <T extends Bar & Foo> void m(T t) {
        foo = t;
    }
}

public class Test {
    public static void main(String[] args) {
        Impl impl = new Impl();
        D d = new D();
        d.m(impl);
    }
}

I don't know how this would fit into Scala, I'm afraid.


[ Edit by Rahul G ]

Scala way is the same. Just the syntax is different.

trait Foo
class Bar
class Impl extends Bar with Foo

class D {
  private var foo: Foo = _
  private var bar: Bar = _

  def m[A <: Bar with Foo](a: A) {
    foo = a
    bar = a
  }
}

object Main {
  def main(args: Array[String]) {
    val impl = new Impl
    val d = new D
    d.m(impl)
  }
}
like image 22
Jon Skeet Avatar answered Mar 30 '23 00:03

Jon Skeet