Java: Как объявить, что переменная реализует интерфейс? - PullRequest
3 голосов
/ 05 июля 2011

В Objective-C я мог бы сделать:

id<HTTPRequestDelegate> delegate;

, чтобы сказать, что delegate (переменная типа id) соответствует протоколу HTTPRequestDelegate (или реализует интерфейс HTTPRequestDelegate на языке Java).

Таким образом, всякий раз, когда я отправляю сообщение, определенное протоколом HTTPRequestDelegate, на delegate, компилятор понимает, что delegate отвечает.

Как мне это сделать, то есть Утиная печать / Динамическая печать на Java?

Ответы [ 8 ]

3 голосов
/ 06 июля 2011

Утиная печать не существует в Java.Если класс реализует интерфейс, он должен объявить, что этот интерфейс реализован.Недостаточно просто иметь методы с той же сигнатурой, что и в интерфейсе.

Интерфейс, однако, является типом, и вы можете объявить переменную этого типа.Например:

List<String> myList;

объявляет переменную myList типа List<String>, где List - интерфейс.

Вы можете инициализировать эту переменную с любым объектом, реализующим этот интерфейс List.:

myList = new ArrayList<String>();

Но тогда ArrayList должен объявить, что он реализует интерфейс List (что он делает).

2 голосов
/ 05 июля 2011
//Static typing
HTTPRequestDelegate delegate;
1 голос
/ 05 июля 2011
Interface a = new Implementation();
0 голосов
/ 06 июля 2011

В Objective-C тип состоит из двух частей: 1) тип указателя класса (например, NSObject *, NSString * и т. Д.);это также может быть id, который является специальным типом, который может принимать любой указатель объекта и отключает предупреждения компилятора статического типа для вызывающих методов;и 2) необязательно, один или несколько протоколов (которые похожи на интерфейсы в Java), которым объект соответствует (например, <NSCopying, NSCoding>)

В Java ссылочным типом является либо имя класса, либо имя интерфейса.(Вы можете выбрать только один.) Между классами и интерфейсами не так много разделения.

В вашем случае тип указателя объекта - id, который не выражает никакой информации, и вы указали один интерфейс, HTTPRequestDelegate.Это может быть эквивалентно выражено в Java как

HTTPRequestDelegate delegate;

Если вы указали более одного протокола или указали фактический тип указателя класса плюс один или несколько протоколов, то ваш тип является «типом пересечения»,пересечение нескольких типов, которые вы указали.В этом случае это будет сложнее, потому что в Java нет простого способа выражения типов пересечений.(Хотя типы пересечения могут быть указаны в границах универсального типа, например, class Foo<T extends Collection & Comparable & CharSequence>)

Кроме этого, единственное другое отличие между Objective-C и Java состоит в том, что в Objective-C вы можете отправлять любое сообщение (т.е. вызывать любой метод) для указателя объекта, и это разрешается, даже если статический тип переменной не указывает на то, что она поддерживается (компилятор выдаст предупреждение, если вы используете фактический тип указателя класса; если вы используете id это не даст предупреждение).Я предполагаю, что это динамическая типизация, о которой вы говорите.В то время как в Java вы можете вызывать только те методы, которые, как известно, поддерживаются статическим типом во время компиляции.

Но если вы используете тип, такой как id<HTTPRequestDelegate>, то есть вероятность, что вы намереваетесь тольков любом случае используйте методы, предоставляемые HTTPRequestDelegate, чтобы не использовать никаких возможностей динамического набора текста.Таким образом, в Java достаточно только HTTPRequestDelegate.

0 голосов
/ 06 июля 2011

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

Для примера List / ArrayList вы можете создать объект ArrayList и использовать его везде, где требуется List, или, на основе других реализованных интерфейсов, Serializable , Cloneable, Iterable, Collection или RandomAccess. Учитывая суперклассы, экземпляр ArrayList может использоваться как AbstractList, AbstractCollection или java.lang.Object.

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

Поскольку это звучит как забавно, вот пример оборачивания не-Утки в прокси-объект.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DuckDemo {
    public static Duck getDuckProxy(final Object duckLike) {
        final InvocationHandler invocationHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> actualClass = duckLike.getClass();
                String methodName = method.getName();
                Class[] parameterTypes = method.getParameterTypes();

                Method requested = actualClass.getDeclaredMethod (methodName, parameterTypes);

                return requested.invoke(duckLike, args);
            }
        };

        final ClassLoader originalObjectClassLoader = duckLike.getClass().getClassLoader();
        Duck woodenDuck = (Duck) Proxy.newProxyInstance(
                originalObjectClassLoader,
                new Class[] { Duck.class },
                invocationHandler
        );

        return woodenDuck;
    }

    private interface Duck {
        void quack();
    };

    public static void makeItQuack (Duck duck) {
        duck.quack();
    }

    public static void main (String args[]) {
        Object quacksLikeADuck = new Object() {
            void quack() {
                System.out.println ("Quack!");
            }
        };

        // Does not compile -- makeItQuack(DuckDemo.Duck) [...] cannot be applied to (java.lang.Object)
        // makeItQuack (quacksLikeADuck);

        // Runtime java.lang.ClassCastException: [...] cannot be cast to GenericProxyFactory$Duck
        // makeItQuack ((Duck)quacksLikeADuck);

        Duck d = getDuckProxy(quacksLikeADuck);

        makeItQuack (d);
    }
}

Что бы это ни стоило, в IBM developerWorks также есть хорошая статья на тему динамических прокси.

0 голосов
/ 06 июля 2011

Я предполагаю, что делегат явно не реализует интерфейс, который вы хотите.

Вы можете создать новый класс, который реализует интерфейс и расширяет требуемый реализующий класс (или имеет реализующий класс и явно вызывает соответствующий метод в интерфейсе).

Если это не то, что вы хотите, возможно, вам нужна здоровая доза размышления. Посмотрите на java.lang.reflect.Proxy и InvocationHandler.

Если вы ищете сокращение, чтобы избежать явной реализации методов для интерфейса, использующего композицию, Java на самом деле не обеспечивает синтаксическую поддержку для этого. Вы должны быть явными.

Если вы хотите пойти по пути отражения (не рекомендуется при дополнительной печати), взгляните на Mockito.

0 голосов
/ 06 июля 2011

Ява не имеет понятия о наборе утки.Вы должны привести экземпляр к известному типу.

0 голосов
/ 05 июля 2011

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

В любом случае, скажем, у вас есть ссылка на экземпляр, который, как вы знаете, реализует HTTPRequestDelegate. Вы можете разыграть это так:

HTTPRequestDelegate delegate = (HTTPRequestDelegate) ref;

Бит в скобках - это актерский состав. Теперь вы можете вызывать методы в delegate (передавать сообщения на языке java) своему сердцу, если они определены в HTTPRequestDelegate.

Другой способ, которым Java-программисты делают вещи типа «утка», - это совершенствование, но если вы знаете интерфейс, то использование оболочки - это путь.

...