Использование полиморфизма вместо instanceof [duplicate]

Да, Android поддерживает Java 8 Теперь (24.1.17)

Теперь это возможно

Но вам нужно будет, чтобы ваше устройство было запущено на java 1.8 и разрешено «jackOptions» «запустить его. Джек - это имя нового Android-компилятора, который запускает Java 8

https://developer.android.com/guide/platform/j8-jack.html

добавьте эти строки в build_gradle

    android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Java 8 кажется запущенным движком java для Android studio 2.0, но он все еще не принимает синтаксис java 8 после того, как я проверил, и теперь вы не можете выбрать компилятор из студии Android. Тем не менее, вы можете использовать плагин scala, если вам нужен механизм функционального программирования в вашем Android-клиенте.

97
задан Sergey Mikhanov 15 October 2009 в 14:19
поделиться

7 ответов

Я ожидаю, что выбор метода учитывает действительный (не объявленный) тип параметра. Я что-то упустил?

Да. Ваши ожидания ошибочны. В Java динамическая отправка метода происходит только для объекта, на который вызывается метод, а не для типов параметров перегруженных методов.

Ссылаясь на Спецификацию языка Java :

Когда метод вызывается (§15.12), количество фактических аргументов (и любых явных аргументов типа) и типы аргументов во время компиляции используются во время компиляции для определения сигнатуры метод, который будет вызываться (§15.12.2). Если метод, который должен быть вызван, является методом экземпляра, фактический метод, который будет вызываться, будет определен во время выполнения, используя динамический поиск метода (§15.12.4).

79
ответ дан Michael Borgwardt 25 August 2018 в 22:50
поделиться
  • 1
    Можете ли вы объяснить спецификацию, которую вы цитировали, пожалуйста. Кажется, что два предложения противоречат друг другу. В приведенном выше примере используются методы экземпляра, но метод, который вызывается, явно не определяется во время выполнения. – Alex Worden 3 May 2012 в 22:19
  • 2
    @Alex Worden: тип времени компиляции параметров метода используется для определения сигнатуры вызываемого метода, в данном случае foo(Object). Во время выполнения класс объекта , вызываемый в , определяет, какая реализация этого метода вызывается, принимая во внимание, что он может быть экземпляром подкласса объявленного типа, который переопределяет метод. – Michael Borgwardt 3 May 2012 в 22:31

Java ищет тип ссылки при попытке определить, какой метод вызывать. Если вы хотите заставить свой код выбрать «правильный» метод, вы можете объявить свои поля как экземпляры определенного типа:

Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();

Вы также можете указать ваши параметры как тип параметра:

callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);
2
ответ дан akf 25 August 2018 в 22:50
поделиться

У меня была аналогичная проблема с вызовом правого конструктора класса под названием «Параметр», который мог бы принимать несколько базовых типов Java, таких как String, Integer, Boolean, Long и т. д. Учитывая массив объектов, я хочу их преобразовать в массив объектов Parameter, вызывая наиболее специфичный конструктор для каждого объекта во входном массиве. Я также хотел определить конструктор Parameter (Object o), который бы выбрал исключение IllegalArgumentException. Я, конечно, нашел, что этот метод вызывается для каждого объекта в моем массиве.

Решение, которое я использовал, - это поиск конструктора через отражение ...

public Parameter[] convertObjectsToParameters(Object[] objArray) {
    Parameter[] paramArray = new Parameter[objArray.length];
    int i = 0;
    for (Object obj : objArray) {
        try {
            Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
            paramArray[i++] = cons.newInstance(obj);
        } catch (Exception e) {
            throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
        }
    }
    return paramArray;
}

Не требуется уродливого экземпляра, операторов switch или шаблона посетителя! :) [/ Д2]

4
ответ дан Alex Worden 25 August 2018 в 22:50
поделиться

Возможность отправки вызова методу, основанному на типах аргументов, называется multiple dispatch . В Java это делается с шаблоном посетителя .

Однако, поскольку вы имеете дело с Integer s и String s, вы не можете легко включить этот шаблон (вы просто не могут изменять эти классы). Таким образом, гигантское switch на время выполнения объекта будет вашим оружием выбора.

14
ответ дан Anton Gogolev 25 August 2018 в 22:50
поделиться

Если существует точное соответствие между количеством и типами аргументов, указанными в вызове метода, и сигнатурой метода перегруженного метода, то это метод, который будет вызываться. Вы используете ссылки на объекты, поэтому java решает во время компиляции, что для параметра Object существует метод, который принимает непосредственно Object. Так он назвал этот метод 3 раза.

0
ответ дан Ashish Thukral 25 August 2018 в 22:50
поделиться

Как упоминалось ранее, во время компиляции выполняется перегрузка разрешения.

Java Puzzlers имеет приятный пример:

Головоломка 46: Случай Confusing Constructor

Эта головоломка представляет вам два конструктора Confusing. Основной метод вызывает конструктор, но какой? Выход программы зависит от ответа. Что делает программа, или она даже законна?

public class Confusing {

    private Confusing(Object o) {
        System.out.println("Object");
    }

    private Confusing(double[] dArray) {
        System.out.println("double array");
    }

    public static void main(String[] args) {
        new Confusing(null);
    }
}

Решение 46: Случай смущающего конструктора

... Процесс разрешения перегрузки Java работает в два этапа. На первом этапе выбираются все доступные или применимые методы или конструкторы. Вторая фаза выбирает наиболее специфичный методов или конструкторов, выбранных на первой фазе. Один метод или конструктор менее конкретный , чем другой, если он может принимать любые параметры, переданные другому [JLS 15.12.2.5].

В нашей программе оба конструктора доступны и применимы , Конструктор Confusing (Object) принимает любой параметр, переданный в Confusing (double []) , поэтому Confusing (Object) менее специфичен. (Каждый двойной массив является Object , но не каждый Object является двойным массивом .) Наиболее специфический конструктор поэтому Confusing (double []) , который объясняет вывод программы.

Такое поведение имеет смысл, если вы передадите значение типа double [] ; это противоречит, если вы передадите null . Ключом к пониманию этой головоломки является то, что тест, для которого метод или конструктор наиболее специфичен, не использует фактические параметры: параметры, появляющиеся в вызове. Они используются только для определения того, какие перегрузки применимы. Когда компилятор определяет, какие перегрузки применимы и доступны, он выбирает наиболее специфическую перегрузку, используя только формальные параметры: параметры, появляющиеся в объявлении.

Чтобы вызвать Confusing (Object) с параметром null , напишите new Confusing ((Object) null) . Это гарантирует, что применим только Confusing (Object) . В более общем плане, чтобы заставить компилятор выбрать конкретную перегрузку, применить фактические параметры к объявленным типам формальных параметров.

78
ответ дан denis.zhdanov 25 August 2018 в 22:50
поделиться
  • 1
    Я надеюсь, что еще не поздно сказать - «одно из лучших объяснений SOFT». Благодаря :) – TheLostMind♦ 19 May 2014 в 10:28
  • 2
    Никогда не поздно для хороших слов :) – denis.zhdanov 19 May 2014 в 13:50
  • 3
    Я считаю, что если мы также добавим конструктор «private Confusing» (int [] iArray), он не сможет скомпилировать, не так ли? Потому что теперь есть два конструктора с той же специфичностью. – Risser 14 October 2014 в 14:49
  • 4
    +1 для "наиболее конкретных" - & GT; все, что мне нужно было знать :) – Fabian Zeindl 31 October 2014 в 15:12
  • 5
    Если я использую динамические типы возврата в качестве ввода функции, он всегда использует менее специфический ... сказал, что метод, который может использоваться для всех возможных возвращаемых значений ... – kaiser 27 October 2016 в 20:01

В Java метод вызова (как в используемой сигнатуре метода) определяется во время компиляции, поэтому он идет с типом времени компиляции.

Типичный шаблон для работы вокруг этого заключается в проверке тип объекта в методе с сигнатурой объекта и делегировать метод с литой.

    public void foo(Object o) {
        if (o instanceof String) foo((String) o);
        if (o instanceof Integer) foo((Integer) o);
        logger.debug("foo(Object o)");
    }

Если у вас много типов, и это неуправляемо, то перегрузка метода, вероятно, не является правильным подходом, скорее публичный метод должен просто взять Object и реализовать какой-то шаблон стратегии для делегирования соответствующей обработки для каждого типа объекта.

11
ответ дан Yishai 25 August 2018 в 22:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: