Различия между интерфейсами Java и протоколами Objective-C?

93

Я знаю Java, а сейчас изучаю Objective-C. В чем именно разница между интерфейсами Java и протоколами Objective-C?

Арне Эвертссон
источник

Ответы:

82

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

Если вы применяете формальный протокол (терминология Objective-C для «реализации интерфейса»), компилятор будет выдавать предупреждения для нереализованных методов, как и следовало ожидать в Java. В отличие от Java (как упоминал скаффман ), если класс Objective-C реализует методы, содержащиеся в формальном протоколе, говорят, что он «соответствует» этому протоколу, даже если его интерфейс явно не принимает его.Вы можете проверить соответствие протокола в коде (используя -conformsToProtocol :) следующим образом:

if ([myObject conformsToProtocol:@protocol(MyProtocol)]) {
    ...
}

ПРИМЕЧАНИЕ. В документации Apple указано:

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

Начиная с Objective-C 2.0 (в OS X 10.5 «Leopard» и iOS) формальные протоколы теперь могут определять дополнительные методы , и класс соответствует протоколу, если он реализует все необходимые методы. Вы можете использовать ключевые слова @required(по умолчанию) и, @optionalчтобы указать, должны или могут быть реализованы следующие объявления методов в соответствии с протоколом. (См. Раздел руководства по языку программирования Apple Objective-C 2.0, в котором обсуждаются дополнительные методы протокола .)

Дополнительные методы протокола открывают разработчикам большую гибкость, особенно для реализации делегатов и слушателей . Вместо того, чтобы расширять что-то вроде MouseInputAdapter (что может раздражать, поскольку Java также является одинарным наследованием) или реализовывать множество бессмысленных, пустых методов, вы можете принять протокол и реализовать только те необязательные методы, которые вам нужны. С помощью этого шаблона вызывающий проверяет, реализован ли метод перед его вызовом (используя -respondsToSelector ) следующим образом:

if ([myObject respondsToSelector:@selector(fillArray:withObject:)]) {
    [myObject fillArray:anArray withObject:foo];
    ...
}

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

Куинн Тейлор
источник
4
«Если вы примете формальный протокол (терминология Objective-C для« реализации интерфейса »), компилятор выдаст предупреждения для нереализованных методов, как и следовало ожидать в Java». В этом случае Java выдаст ошибку, а не предупреждение.
Раффи Хатчадурян,
3
«если класс Objective-C реализует методы, содержащиеся в формальном протоколе, говорят, что он« соответствует »этому протоколу, даже если его интерфейс не принимает его явно. Вы можете проверить соответствие протокола в коде (используя -conformsToProtocol: ) вот так "Это ЛОЖЬ. -conformsToProtocol:вернет YES, только если класс явно принимает протокол. Вы хоть пробовали?
user102008
2
Вы правы, -conformsToProtocol:действительно требуется, чтобы класс (или предок) официально объявил, что он принимает протокол. Не знаю, как я ошибся, спасибо за исправление!
Куинн Тейлор
18

Они почти идентичны. Однако одна вещь, которая меня уловила, заключается в том, что если вы явно не заявите, что объективный протокол C также реализует NSObject, ссылки на этот протокол не получают доступа к методам, которые объявляет NSObject (без предупреждения компилятора в любом случае). С java вы можете иметь ссылку на интерфейс и по-прежнему вызывать на нем toString () и т. Д.

например

Цель C:

@protocol MyProtocol
// Protocol definition
@end

id <MyProtocol> myProtocol;

 [myProtocol retain] // Compiler warning

Ява:

public interface MyInterface {
// interface definition
}

MyInterface myInterface;

myInterface.toString();  // Works fine.

Цель C (фиксированная):

@protocol MyProtocol <NSObject>
// Protocol definition
@end

id <MyProtocol> myProtocol;

[myProtocol retain] // No Warning
Том Джефферис
источник
25
Это потому, что id и NSObject не совпадают . В Java корневым объектом является Object. В Objective-C, NSObject является корневым объектом, но не корневой объект. Если вам нужен доступ ко всем методам NSObject (методам класса, а также протоколам), укажите это явно: NSObject <MyProtocol> myProtocol; вместо: id <MyProtocol> ... Когда вы используете id, вы говорите: меня не волнует объект, а только протокол, что в вашем случае неверно.
Джейсон Коко