Автоматическое преобразование типов в Java?

Есть ли способ сделать автоматическое неявное преобразование типов в Java? Например, скажем, у меня есть два типа: «FooSet» и «BarSet», которые оба являются представлениями набора. Между типами легко конвертировать, поэтому я написал два служебных метода:

/** Given a BarSet, returns a FooSet */
public FooSet barTOfoo(BarSet input) { /* ... */ }

/** Given a FooSet, returns a BarSet */
public BarSet fooTObar(FooSet input) { /* ... */ }

Теперь скажем, что есть такой метод, который я хочу вызвать:

public void doSomething(FooSet data) {
    /* .. */
}

Но все, что у меня есть, это BarSet myBarSet... это означает дополнительный набор текста, например:

doSomething(barTOfoo(myBarSet));

Есть ли способ сообщить компилятору, что определенные типы могут быть автоматически преобразованы в другие типы? Я знаю, что это возможно в C++ с перегрузкой, но я не могу найти способ в Java. Я хочу просто набрать:

doSomething(myBarSet);

И компилятор знает, что нужно автоматически вызывать barTOfoo()


person davr    schedule 09.03.2010    source источник
comment
Подробнее... У меня есть две отдельные библиотеки, которые я не могу изменить, и обе они используют похожие, но несовместимые структуры данных. Мне часто нужно получить данные из одной библиотеки, обработать их, а затем передать в другую библиотеку. Мой код оказывается заваленным вызовами fooTObar и barTOfoo повсюду.   -  person davr    schedule 09.03.2010
comment
Этот тип связующего звена между похожими, но не совсем совместимыми API определенно не является одной из сильных сторон Java. Существует много шаблонов, чтобы сделать это.   -  person Yishai    schedule 09.03.2010
comment
Если кому-то интересно, эти две библиотеки представляют собой объекты на основе JSON (для связи с веб-службой) и объекты на основе AMF (для связи с клиентами Flash через постоянное соединение RMTP). И в AMF, и в JSON есть понятия массивов, списков, ассоциативных массивов, объектов и т. д. Поэтому в Java я постоянно конвертирую между AMFDataArray и JSONArray, AMFDataObj и JSONObject и т. д.   -  person davr    schedule 10.03.2010


Ответы (6)


Ответ короткий: это возможно с перегрузкой в ​​C++, но нет способа сделать это в Java.

person Martin    schedule 09.03.2010
comment
Также возможно в C# и многих других разумных языках, потому что это очень полезная функция. - person Tomáš Zato - Reinstate Monica; 27.01.2015

Вы можете перегрузить свои методы, например:

public void doSomething(FooSet data) {
    /* .. */
}

public void doSomething(BarSet data) {
    doSomething(barTOfoo(data));
}
person masher    schedule 09.03.2010
comment
doSomething — это метод, который я не могу изменить, он находится во внешней библиотеке. Но я предполагаю, что написание собственных перегруженных методов, которые вызывают внешний метод, является вариантом. - person davr; 09.03.2010
comment
@davr да, я видел это редактирование после того, как написал свой ответ. Я полагаю, что еще один слой между вашим кодом и библиотекой может быть немного неприятным, но это зависит от того, насколько неприятными являются barTOfoo и fooTObar... Это также позволит вам изменить способ реализации кода библиотеки, если его функциональность меняется. - person masher; 09.03.2010

Вы можете сделать и то, и другое вместе:

(1) Напишите методы-оболочки, которые выполняют преобразование в фоновом режиме.

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

Это избавит вас от постоянного преобразования между двумя типами и, возможно, от проблем с производительностью. Даже если вы пойдете против кеширования, я бы все же рекомендовал (как сказали другие авторы) использовать методы упаковки. Это, по крайней мере, сэкономит вам много ненужного набора текста.

Удачи.

person dimitarvp    schedule 09.03.2010

Перегрузка работает противоположным образом, вы объявляете два метода в объекте-получателе:

public void doSomething(FooSet data)
{
    /* .. */
}

public void doSomething(BarSet data)
{
    doSomething(barToFoo(data));
}

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

Конечно, перегрузка в Java ограничивается объектным уровнем (поскольку вызовы ограничены экземплярами или объявлениями классов), но это работает точно так же.

В вашем случае вы можете попробовать расширить класс также, если он находится во внешней библиотеке, поскольку это java, вы должны быть в состоянии это сделать и добавить метод или с помощью reflection (руководство по Java), чтобы добавить метод динамически.

person Jack    schedule 09.03.2010
comment
doSomething — это метод, который я не могу изменить, он находится во внешней библиотеке. Но я предполагаю, что написание собственных перегруженных методов, которые вызывают внешний метод, является вариантом. - person davr; 09.03.2010
comment
Это ни в коем случае не пример динамического связывания. Это просто перегрузка, это происходит во время компиляции на основе объявленных типов переменных. - person vava; 09.03.2010

Автоматическое преобразование типов не поддерживается (вам нужен неявные преобразования типов для этого).

Но вы можете попробовать использовать тип Variant в качестве соединительной коробки для переключения между типами:

/** Given a BarSet, returns a FooSet */
public Function<BarSet, FooSet> barTofoo = new Function<BarSet, FooSet>() {
    @Override public FooSet apply(BarSet input) { /* ... */ }
}

/** Given a FooSet, returns a BarSet */
public Function<FooSet, BarSet> fooToBar = new Function<FooSet, BarSet>() {
    @Override public BarSet apply(FooSet input) { /* ... */ }
}

/** Create a type conversion context in which both of these conversions are registered */
TypeConversionContext fooBarConversionContext = MatchingTypeConversionContext.builder()
    .register(FooSet.class, BarSet.class, fooToBar)
    .register(BarSet.class, FooSet.class, barToFoo)
    .build();

/** Put a FooSet into a Variant, bound to our type conversion context */
FooSet fooSet = new FooSet();
Variant data = Variant.of(fooSet).in(fooBarConversionContext);

/** Pull a BarSet out of the Variant */
public void doSomething(Variant data) {
    Preconditions.checkArgument(data.isConvertibleTo(BarSet.class);
    BarSet barSet = data.as(BarSet.class);
    // ...
}
person Dominic Fox    schedule 09.08.2012
comment
Это интересно, однако для меня это типичная многословность Java. В конечном итоге я бы усложнял вещи вместо того, чтобы упрощать их, как я хотел. - person davr; 11.08.2012

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

person vava    schedule 09.03.2010
comment
Я думаю, это объясняет, почему вы должны использовать String.equals вместо == все время... так глупо. - person davr; 24.03.2010
comment
@davr, именно, они хотели, чтобы вы сразу заметили, это ссылки или сравнивается фактический контент. - person vava; 24.03.2010