Полиморфные методы VS поллиморфный instanceof

162
28 февраля 2018, 08:42

При написании кода столкнулся с вопросом: а что же все таки быстрее? Набросал тест:

import java.util.*;
public class PolymorphTest {
    public static void main(String[] args) {
        if (args.length == 0) {
            exit();
        }
        int count;
        try {
            count = Integer.parseInt(args[0]);
        } catch (Exception e) {
            count = 0;
            exit();
        }

        List<Valued> list = Geneartor.generate(count);
        List<A> listA = Geneartor.fetchA(list);
        List<B> listB = Geneartor.fetchB(list);
        List<C> listC = Geneartor.fetchC(list);
        int sum = 0;
        long millisStart = System.currentTimeMillis();
        for (Valued o : list) {
            sum += InstanseChecker.value(o);    
        }
        long millisEnd = System.currentTimeMillis();
        long millis = millisEnd - millisStart;
        System.out.println("Seconds = " + ((millis) / 1000) + ", millis = " + millis);

        millisStart = System.currentTimeMillis();
        for (A o : listA) {
            sum += Polymorph.value(o);  
        }
        for (B o : listB) {
            sum += Polymorph.value(o);  
        }
        for (C o : listC) {
            sum += Polymorph.value(o);  
        }
        millisEnd = System.currentTimeMillis();
        millis = millisEnd - millisStart;
        System.out.println("Seconds = " + ((millis) / 1000) + ", millis = " + millis);
    }
    private static void exit() {
        System.out.println("Need to enter Integer arg for elements count"); 
        System.exit(0);
    }
}
class Geneartor {
    public static List<Valued> generate(int count) {
        List<Valued> list = new ArrayList<Valued>();
        for (int i = 0; i < count; i++) {
            int next = new Random().nextInt(3);
            switch (next) {
                case 0:
                    list.add(new A());
                    break;
                case 1: 
                    list.add(new B());
                    break;
                case 2:
                    list.add(new C());
                    break;
            }
        }
        return list;
    }
    public static List<A> fetchA(List<Valued> list) {
        List<A> listA = new ArrayList<A>();
        for (Valued o : list) {
            if (o instanceof A) {
                listA.add((A) o);
            }
        }
        return listA;
    }
    public static List<B> fetchB(List<Valued> list) {
        List<B> listB = new ArrayList<B>();
        for (Valued o : list) {
            if (o instanceof B) {
                listB.add((B) o);
            }
        }
        return listB;
    }
    public static List<C> fetchC(List<Valued> list) {
        List<C> listC = new ArrayList<C>();
        for (Valued o : list) {
            if (o instanceof C) {
                listC.add((C) o);
            }
        }
        return listC;
    }
}
class Polymorph {
    static int value(A a) {
        return a.level();
    }
    static int value(B b) {
        return b.rate();
    }
    static int value(C c) {
        return c.value();
    }
}
class InstanseChecker {
    static int value(Valued valued) {
        if (valued instanceof A) {
            return ((A) valued).level();
        } else if (valued instanceof B) {
            return ((B) valued).rate();
        } else if (valued instanceof C) {
            return ((C) valued).value();
        }
        return 0;
    }
}
interface Valued {}
class A implements Valued {
    int level = 0;
    int level() {
        return level;
    }
}
class B implements Valued {
    int rate = 0;
    int rate() {
        return rate;
    }
}
class C implements Valued {
    int value = 0;
    int value() {
        return value;
    }
}

Кому лень смотреть код: В двух словах: Есть три типа, A, B, C. У каждого есть метод, который возвращает значение. (В силу обстоятельств я не могу назвать эти методы у себя в коде одинаково и там они вообще будут интерфейсными типами). Но мне нужен один интерфейс для получения значения каждого из этих типов. Тест описывает три таких объекта. Создаётся список экземпляров, совершенно произвольно, он же бьётся на три списка - конкретных объектов. Есть набор полиморфных методов, с одинаковыми названиями и возвращаемым типом, но разными аргументами - типами этих объектов, и один метод, который принимает общий тип и проверяет с помощью instanceof тип конкретный, и вызывает у объектов соответствующий метод.

Результаты меня немного удивили.

Я почему то ожидал, что вызов полиморфных методов будет выполняться приблизительно с той же скоростью, что и вызов instanceof. Однако я получил результат, и буду использовать именно полиморфные методы.

Вопрос же в следующем - где можно почитать, почему так происходит и поизучать кишки этого действа?

Answer 1

Связывание при перегрузке (overloading) метода происходит на этапе компиляции. Для примера, что выведет следующий код?

public void m1(Object a) {
    System.out.println("Object");
}
public void m1(Integer a) {
    System.out.println("Integer");
}
@Test
public void test() {
    Integer i = 3;
    Object o = i;
    m1(i);
    m1(o);
}

Не смотря на то, что фактический тип будет одинаковый, вывод будет следующим:

Integer
Object

То есть никакой дополнительной работы в рантайме не выполянется. В вашем первом случае еще и дополнительно работает instanceof.

Можете еще почитать про паттерн "Visitor" (посетитель) и узнаете еще про один способ опередления типа, хотя штука громоздкая, применять нужно аккуратно.

READ ALSO
Tomcat websocket задать размер буфера для передачи blob

Tomcat websocket задать размер буфера для передачи blob

при передачи по ws больших объёмов на выскакивает такое

154
SAXParseException; lineNumber: 1; columnNumber: 1; Premature end of file

SAXParseException; lineNumber: 1; columnNumber: 1; Premature end of file

При отправке сообщения с xml контетном в наследуемом от AbstractPhaseInterceptor классе возникает ошибка SAXParseException; lineNumber: 1; columnNumber: 1; Premature end of file

199
Вопрос по поводу реднеринга в JSF &lt;f:ajax

Вопрос по поводу реднеринга в JSF <f:ajax

Код, который нужно обновить :

146
Задать кодировку консоли Spring Shell

Задать кодировку консоли Spring Shell

Мой метод Shell должен вернуть символы Кириллицы:

173