Способ узнать, как был инициализирован объект Java вместо объявленного типа?

Я не знаю, упустил ли я что-то здесь, но у меня возникли проблемы с приведением объекта к его фактическому инициализированному типу. По сути, если я создаю объект с «SuperClass sc = new SubClass()», то я вызываю метод в sc, я хочу, чтобы метод мог вызывать метод (подкласс) вместо метода (суперкласс). Пример показан ниже:

public class Example
{
    public static void act(SuperClass a) {
        System.out.println("SuperClass");
    }

    public static void act(SubClass a) {
        System.out.println("SubClass");
    }

    public static void main(String[] args) {
        SuperClass sc = new SubClass();

        // want to find a way to call act(SubClass) instead of act(SuperClass)
        act(sc);
   }
}

class SuperClass {}
class SubClass extends SuperClass {}

Я использую шаблон посетителя прямо сейчас, но мне интересно, есть ли другие способы сделать это, возможно, через Java Reflection API?

Заранее большое спасибо!

== редактировать ==

Я знаю, что, как правило, с объектно-ориентированным подходом лучше привязать функциональность к самим суперклассам/подклассам, но для моих конкретных случаев использования у меня есть куча подклассов, которые являются неизменяемыми классами моделей, которые должны быть переданы различным типам механизмов выполнения (подумайте разные классы «Пример»). Классы подклассов/моделей должны содержать только неизменяемую информацию, не более того, а фактическая реальная функциональность связана с механизмом выполнения (класс примера). Вот почему я задаюсь вопросом об альтернативах шаблону посетителя. У кого-нибудь есть способ восстановить фактическую «инициализированную» информацию в Java? Если да, то большое спасибо.

И из-за характера проблемы я не могу использовать прямое приведение типов... Представьте, что у меня есть массив суперклассов, где каждый элемент может быть подклассом1, подклассом2, подклассом3, и все они простираются от суперкласса.

Теперь, когда вы вытаскиваете элементы из Arraylist, вы получаете объект SuperClass, даже если на самом деле это могут быть SubClass1, SubClass2, SubClass3 и т. д.

Затем я хочу вызвать act(SubClass1) и иметь возможность вызывать правильный метод act() для текущего типа. Итак, я хочу в конечном итоге вызвать действие (SubClass1), действие (SubClass2), действие (SubClass3) вместо действия (SuperClass).

== изменить еще раз ==

Я придумал способ сделать это с помощью Java Reflection API, найдя фактический базовый тип подкласса с помощью Class.forName(имя класса), а затем динамически вызывая метод с правильной подписью метода. Я написал это в форме ответа где-то на этой странице для тех, кто интересуется этой проблемой. Обратите внимание, что это не очень эффективный способ выполнить то, что я пытаюсь сделать здесь, и вам, вероятно, лучше использовать шаблон посетителя или операторы if-else, если вы застряли в моей ситуации.


Таким образом, ответ, который дал Никола Мусатти, наиболее близок к ответу на мой вопрос, хотя, как он также указал, по мере роста числа подклассов список операторов if-else становится очень длинным. Я выберу его ответ в качестве принятого ответа, поскольку в своем вопросе я четко не указал, что надеялся избежать проверок if-else.

В любом случае, сегодня я немного поиграл с Java Reflection API и придумал следующее:

SuperClass sc = new SubClass();

// Get the actual class of sc. actualClass now is SubClass.
Class actualClass = Class.forName(sc.getClass().getCanonicalName());

// Basically invoking act(SubClass sc) instead of act(SuperClass sc)
Class parameters[] = {actualClass};
Method method = Example.class.getMethod("act", parameters);
Object arguments[] = {sc};
method.invoke(null, arguments);

Это, безусловно, не лучший способ сделать что-то, особенно из-за потери производительности, налагаемой Java Reflection API. Это может быть лучше, чем шаблон посетителя или проверка if-else, если у вас есть миллион подклассов, поскольку, вероятно, для управления требуется меньше кода, однако пока я буду придерживаться шаблона посетителя, поскольку у меня нет миллиона подклассов для управления .

В любом случае, я просто подумал, что опубликую это здесь, чтобы показать, что это можно сделать, просто для тех, кому любопытно.


person user872831    schedule 01.08.2011    source источник
comment
ты извращаешь ОО. Это своего рода один из основных моментов объектно-ориентированного программирования: вы передаете сообщение, а объект сам знает, как ответить на это сообщение. Здесь вы создаете Пример, который слишком много знает о деталях реализации и пытается взять на себя ответственность за действие, которое должно быть выполнено объектом. Я почти уверен, что вы далеко не в том, что вы пытаетесь сделать здесь.   -  person SyntaxT3rr0r    schedule 01.08.2011
comment
Классы, которые у меня есть (куча подклассов), должны быть неизменяемыми классами моделей, которые передаются и используются разными движками. Предполагается, что они хранят информацию, не более того, и поэтому я не хочу добавлять в них какие-либо другие функции. По сути, у меня должно быть много разных примеров (движков), которые действуют на разные связанные подклассы (классы моделей).   -  person user872831    schedule 01.08.2011
comment
Неизменяемость не означает, что не может быть методов доступа. То, что вам нужно будет добавить, будет не «функциональностью», а просто полиморфным средством доступа к данным внутри.   -  person Paweł Obrok    schedule 01.08.2011
comment
Как я уже сказал, у меня много разных движков примеров, и я не хочу добавлять метод для каждого движка исполнения в свои классы моделей. Это также не имеет смысла, поскольку классы моделей - это просто... классы моделей. Механизм выполнения должен выполнять работу. Зачем загромождать мои классы моделей всеми этими дополнительными методами и вносить в них изменения всякий раз, когда я делаю новые движки?   -  person user872831    schedule 01.08.2011


Ответы (4)


Общее решение — шаблон «Посетитель». Если у вас есть конкретная ситуация, когда вы знаете фактический тип sc, вы действительно можете использовать приведение, как уже было предложено, возможно, перед проверкой типа, как в

if ( sc instanceof SubClass ) act((SubClass)sc);

Однако шаблон Посетитель был изобретен потому, что этот подход не масштабируется, когда количество классов, которые вам нужно обработать, увеличивается.

И последнее, но не менее важное: иногда самый простой подход — просто сохранить переменную фактического типа:

SubClass scc = new SubClass();
SuperClass sc = scc;
act(scc);
person Nicola Musatti    schedule 01.08.2011

Разве вы не должны определить act() в SuperClass и SubClass? Таким образом, правильный метод будет вызываться независимо от типа ссылки на объект.

Редактировать: если я правильно помню, шаблон посетителя определяет что-то вроде метода accept() для посещаемых элементов, который позволяет посетителю полиморфно получать доступ ко всему, что его интересует в посещенных элементах.

person Paweł Obrok    schedule 01.08.2011
comment
да, у моих подклассов есть метод accept(). просто исключил их из примера для краткости - person user872831; 01.08.2011
comment
Таким образом, все, что нужно сделать посетителю, должно выполняться с помощью метода accept(). - person Paweł Obrok; 01.08.2011
comment
да, я говорю, что хочу знать об альтернативах шаблону посетителя, то есть об удалении этих методов accept() и возможности прямого вызова act(SubClass). Мне было интересно, есть ли в Java Reflection API вещи, которые могли бы это сделать. Я смотрел на Class.cast(), но не смог заставить его работать. - person user872831; 01.08.2011
comment
Похоже, у вас проблемы - вы пытаетесь реализовать полиморфизм, используя что-то еще, кроме полиморфизма. Если это одноразовое приложение, вы можете использовать Reflection, cast, что угодно, но я бы посоветовал не использовать его в чем-то, что предназначено для повторного использования. - person Paweł Obrok; 01.08.2011
comment
Ну, даже если из любопытства посмотреть, можно ли это сделать, есть ли какие-нибудь способы сделать это? - person user872831; 01.08.2011
comment
В этом случае проверьте ответ Николы Мусатти. - person Paweł Obrok; 02.08.2011

Как насчет

act((SubClass)sc);

??

person Roman Pietrzak    schedule 01.08.2011

Возможно, вам следует сделать метод act членом класса SupperClass, а затем переопределить его в классе SubClass.

person Alina Danila    schedule 01.08.2011
comment
Все методы в Java виртуальные - person Max; 10.09.2012
comment
Я борюсь со своим фоном C++. Спасибо! - person Alina Danila; 10.09.2012