перегрузка метода и полиморфизм - что является более чистым способом сделать это? - PullRequest
1 голос
/ 05 августа 2011

Проверьте следующий пример кода:

public class Test
{
    private void process(Instance1 obj)
    {
        System.out.println("Process Instance 1");
    }

    private void process(Instance2 obj)
    {
        System.out.println("Process Instance 2");
    }

    /* 
    COMMENT THIS OUT - I DON'T HAVE THIS CODE IN REAL LIST. Only here to prove point 3 below calls this
    private void process(SuperClass obj)
    {
        System.out.println("Process superclass");
    }
    */

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        Test test = new Test();
        Instance1 instance1 = test.new Instance1();
        Instance2 instance2 = test.new Instance2();
        SuperClass instance3 = test.new Instance1();

        // test #1          
        test.process(instance1);
        // test #2          
        test.process(instance2);
        // test #3 (compiler error unless there is a process(SuperClass obj) call)
        test.process(instance3);
        // test #4                      
        if(instance3 instanceof Instance1)
            test.process((Instance1)instance3);
        else if(instance3 instanceof Instance2)
            test.process((Instance2)instance3);

    }

    abstract class SuperClass
    {
    }

    class Instance1 extends SuperClass
    {
    }

    class Instance2 extends SuperClass
    {
    }
}

Это дает вывод:

Process Instance 1
Process Instance 2
Process superclass
Process Instance 1

Я надеялся, что тест № 3 будет знать, чтобы вызвать правильную функцию, но кажется, что этоне.Я полагаю, что это время компиляции, а не время выполнения.Вариант № 4 работает, но уродлив, и я надеюсь на лучший путь.

Обновление: чтобы прояснить проблему ... У меня есть абстрактный класс, в котором существует две конкретные реализации.Я хотел бы иметь два перегруженных метода в другом классе (по одному для каждого конкретного класса) и иметь возможность вызывать его без каких-либо уродств.Из того, что я знаю сейчас, это невозможно, потому что это проблема времени компиляции, и компилятор, очевидно, не знает, что это за конкретный класс, когда он не является строго типизированным.

Ответы [ 6 ]

3 голосов
/ 05 августа 2011

Это не вопрос полиморфизма. Это вопрос перегрузки метода.

Когда вы передаете instance3 в process(), он будет вызывать process(SuperClass obj), потому что, насколько JVM знает, instance3 - это SuperClass, поскольку вы объявили его как.

Если вы хотите получить желаемое поведение (тест № 3 выводит «Экземпляр процесса 1»), вы должны определить свой метод process() следующим образом:

private void process(SuperClass obj)
{
    System.out.println("Process " + obj.name());
}

А твои классы такие:

abstract class SuperClass
{
    String name() {
        return "SuperClass";
    }
}

class Instance1 extends SuperClass
{
    String name() {
        return "Instance 1";
    }
}

class Instance2 extends SuperClass
{
    String name() {
        return "Instance 2";
    }
}

Это будет работать из-за динамической (или поздней) привязки метода.

0 голосов
/ 05 августа 2011

Как уже отмечали другие, это не то, что вы можете сделать в Java.Если вам абсолютно необходимо, вы можете использовать рефлексию, чтобы сделать что-то вроде:

private void process(SuperClass obj)
{
  try
  {
     Class<? extends SuperClass> klass = obj.getClass();
     Method m = this.getClass().getMethod("process", klass);
     m.invoke(this, obj);
  }
  catch (Exception e)
  {
     // default behavior
     System.out.println("Process superclass");
  }
}

Однако я действительно последую одному из других предложений.

0 голосов
/ 05 августа 2011

Это то, чем я сейчас занимаюсь. По сути, немного вывернуть проблему наизнанку. Не уверен, считается ли это шаблоном посетителя.

По сути, я просто добавил абстрактную функцию executeProcessor (Test test) в SuperClass, и конкретные классы неявно знают, какую функцию вызывать, чтобы они могли просто использовать приведение.

Что ты думаешь? Этот код пахнет? Или аккуратное решение?

public class Test
{
    private void process(Instance1 obj)
    {
        System.out.println("Process Instance 1");
    }

    private void process(Instance2 obj)
    {
        System.out.println("Process Instance 2");
    }

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        Test test = new Test();

        // Test 1
        Instance1 instance1 = test.new Instance1();
        instance1.executeProcessor(test);

        // Test 2
        Instance2 instance2 = test.new Instance2();
        instance2.executeProcessor(test);

        // Test 3 (note this is not strongly typed)
        SuperClass instance3 = test.new Instance1();
        instance3.executeProcessor(test);
    }

    abstract class SuperClass
    {
        public abstract void executeProcessor(Test test);
    }

    class Instance1 extends SuperClass
    {

        @Override
        public void executeProcessor(Test test)
        {
            test.process((Instance1)this);
        }
    }

    class Instance2 extends SuperClass
    {

        @Override
        public void executeProcessor(Test test)
        {
            test.process((Instance2)this);
        }
    }
}
0 голосов
/ 05 августа 2011

Можете ли вы изменить SuperClass и Instance с?Если это так, вы должны объявить абстрактный метод SuperClass.process() и реализовать его в подклассах.

Чтобы тест №3 работал, вы можете добавить приведение: test.process((Instance1)instance3);.Ужасно.

Вы также можете определить фабричный класс и инкапсулировать переключение типов внутри него.У вас будет ProcessFactory с методом ProcessObject getInstance(SuperClass obj), который возвращает объекты типа ProcessObjectInstance1 или ProcessObjectInstance2, с методом process().ProcessFactory будет работать так же, как ваш тест № 4.

0 голосов
/ 05 августа 2011

Пара предложений для более чистого способа делать то, что вы делаете:

Варианты 1: Абстрактный метод Определите process() для Superclass и переопределите его для Instance1 и Instance2:

abstract class SuperClass
{
    public void process(String[] args)
    {
        System.out.println("Process superclass");
    }
}

class Instance1 extends SuperClass
{
    @Override
    public void process(String[] args)
    {
        System.out.println("Process Instance 1");
    }
}

class Instance2 extends SuperClass
{
    @Override
    public void process(String[] args)
    {
        System.out.println("Process Instance 2");
    }
}

Вариант 2: Шаблон посетителя Определите класс, который выполняет обработку, и является InstanceVisitor

public interface InstanceVisitor
{

    public void processSuperclass(Superclass obj, String[] args);

    public void processInstance1(Instance1 obj, String[] args);

    public void processInstance2(Instance2 obj, String[] args);

}

public class InstanceProcessor implements InstanceVisitor
{

    public void processSuperclass(Superclass obj, String[] args)
    {
        System.out.println("Process superclass");
    }

    public void processInstance1(Instance1 obj, String[] args)
    {
        System.out.println("Process Instance 1");
    }

    public void processInstance2(Instance2 obj, String[] args)
    {
        System.out.println("Process Instance 2");
    }

}

Затем каждый подкласс принимает посетителя и вызывает правильный метод:

abstract class SuperClass
{
    public void accept(InstanceVisitor v, String[] args)
    {
        v.visitSuperclass(this, args);
    }
}

class Instance1 extends SuperClass
{
    @Override
    public void accept(InstanceVisitor v, String[] args)
    {
        v.visitInstance1(this, args);
    }
}

class Instance2 extends SuperClass
{
    @Override
    public void accept(InstanceVisitor v, String[] args)
    {
        v.visitInstance2(this, args);
    }
}
0 голосов
/ 05 августа 2011

Либо сделайте это виртуальной функцией внутри иерархии:

abstract class SuperClass
{
    public void process() { System.out.println("Process super class"); } 
}

class Instance1 extends SuperClass
{
    public void process() { System.out.println("Process instance1"); } 
}

class Instance2 extends SuperClass
{
    public void process() { System.out.println("Process instance2"); } 
}

или различные подходы к множественной отправке, которые изначально не поддерживаются в Java.

...