Comments 119
Проверил в java. Такое же поведение как у С++. PHP такой PHP)
У одноимённых методов может быть разная область видимости и разный набор входных переменных. Тогда действительно неясно становиться, какой из методов наследовать. Интересно, как это решенов C++ и Java?
«У одноимённых методов может быть разная область видимости и разный набор входных переменных.» это перегрузка методов en.wikipedia.org/wiki/Method_overloading, в PHP не реализована. А в java интерфейсы с одинаковыми методами имплементируются без проблем.
Ну вы сами ответили на свой вопрос. Всё дело в перегрузке методов, которая в PHP не реализована.
interface someInterface{
public function someMethod(Bar $bar, array $options, $property);
}
interface anotherInterface{
public function someMethod();
}
class Foo implements someInterface,anotherInterface {
public function someMethod(){
echo 'someMethod() was called'.PHP_EOL;
}
}
Какой из методов родительских интерфейсов Foo должен имплементировать?
сигнатуры методов не совпадают, это два разным метода с точки зрения ооп. Например то же самое в Java выглядит так:
то есть классу необходимо имплементировать оба метода. Но еще раз в PHP нет перегрузки методов. Я же говорил про методы с одинаковыми сигнатурами (кажется это так называется).
public interface someInterface {
public void someMethod(Object object, Iterator iterator);
}
public interface anotherInterface {
public void someMethod();
}
public class fooClass implements anotherInterface,someInterface {
/**
* @param args
*/
public static void main(String[] args) {
fooClass fooClass = new fooClass();
fooClass.someMethod();
}
@Override
public void someMethod() {
System.out.println("someMethod() was called");
}
@Override
public void someMethod(Object object, Iterator iterator) {
System.out.println("someMethod(Object object, Iterator iterator) was called");
}
}
то есть классу необходимо имплементировать оба метода. Но еще раз в PHP нет перегрузки методов. Я же говорил про методы с одинаковыми сигнатурами (кажется это так называется).
С точки зрения ООП или конкретной реализации?
Да в PHP «своя» реализация ооп, правда сильно отличающаяся от таковой в C++/Java/C#/ и так далее.
Ну покажите мне спецификацию ООП, в которой написано, что если параметры методов не совпадают, то это 2 разных метода.
спецификацию я вряд ли найду, но отвечу вам что в любом языке программирования поддерживающем перегрузку методов, перегружаемые методы различаются числом/типом параметров, то есть сигнатурами.
Вообще-то компиляторы при перегрузке обычно создают именно разные методы. Они просто выглядят одинаково для удобства разработчика.
en.wikipedia.org/wiki/Method_overloading
en.wikipedia.org/wiki/Method_overloading
У ООП есть спецификация?
Ну автор же заявляет, что
это два разным метода с точки зрения ооп
Хм, что то я никак не пойму чего вы добиваетесь. en.wikipedia.org/wiki/Method_signature «In computer programming, especially object-oriented programming, a method is commonly identified by its unique method signature. This usually includes the method name, and the number, types and order of its parameters» отсюда получаем что различное число параметров, различные их типы у одноименных методов === различные методы
В топике методы у интерфейсов идетичные, и интерпретатор это прекрасно знает. Почему бы и нет?
Логично ведь что он должен был просто проверить _валидность метода по отношению к интерфейсным описаниям_, а не проверято повторяются ли описания в интерфейсах?
Логично ведь что он должен был просто проверить _валидность метода по отношению к интерфейсным описаниям_, а не проверято повторяются ли описания в интерфейсах?
Все очень просто:
в Java вы можете написать так:
А в PHP такой код вызовет ошибку:
Ну вы поняли почему ;)
в Java вы можете написать так:
interface Test {
void test(int a);
void test(int a, int b);
}
А в PHP такой код вызовет ошибку:
interface Test {
function test($a) {}
function test($a, $b) {}
}
Ну вы поняли почему ;)
Извините, без {} конечно же.
Странно, что перегрузка не реализована в пхп. Что мешает интерпретатору отличать сигнатуры функций не только по имени, но и по количеству параметров?
Потому что:
а) есть возможность задавать необязательные параметры ( function test($a = 42) ),
б) есть магические способы передачи бОльшего количества параметров, чем объявлено (с использованием func_get_args например)
а) есть возможность задавать необязательные параметры ( function test($a = 42) ),
б) есть магические способы передачи бОльшего количества параметров, чем объявлено (с использованием func_get_args например)
Необязательные параметры особой роли не играют — они есть и в C++, где перегрузка, конечно же, существует.
А вот произвольное количество аргументов — это уже существенно.
А вот произвольное количество аргументов — это уже существенно.
Переменное колчиество аргументов в C++ тоже возможно.
Да, но там оно задается специальным синтаксисом, а в php все функции по умолчанию могут принимать переменное количество аргументов.
Как говаривал В. И. Ленин, «учить Си, учить Си и еще раз учить Си».
Вот такой код компилируется и работает и в Си, и в Си++:
В этом примере мы вызываем функцию test1() с лишним параметром, а функцию test2() вообще без параметров, хотя ей как раз их нужно 2. Функция printf штатно принимает произвольное количество параметров; зато наша функция main ничего не возвращает и не принимает, хотя по стандарту должна получить 2 параметра и вернуть int.
Мощь способа вызова функций в стиле Си ( __cdecl ) как раз в том и состоит, что любую функцию можно вызвать с любым количеством параметров.
Вот такой код компилируется и работает и в Си, и в Си++:
int test1()
{
return 42;
}
int test2(int a,int b)
{
return a+b;
}
void main ()
{
int (*f)() = test2; // указатель на функцию
printf ("test results: %d, %d \n", test1(2), f());
}
В этом примере мы вызываем функцию test1() с лишним параметром, а функцию test2() вообще без параметров, хотя ей как раз их нужно 2. Функция printf штатно принимает произвольное количество параметров; зато наша функция main ничего не возвращает и не принимает, хотя по стандарту должна получить 2 параметра и вернуть int.
Мощь способа вызова функций в стиле Си ( __cdecl ) как раз в том и состоит, что любую функцию можно вызвать с любым количеством параметров.
Где он компилируется? g++ жалуется на
test.cpp:11:12: error: ‘::main’ must return ‘int’
test.cpp: In function ‘int main()’:
test.cpp:12:14: error: invalid conversion from ‘int (*)(int, int)’ to ‘int (*)()’
test.cpp:13:43: error: too many arguments to function ‘int test1()’
test.cpp:3:5: note: declared here
А причем тут специальный синтаксис? Давайте рассмотрим частный случай, который сводится к «все функции по умолчанию могут принимать переменное количество аргументов»: во все без исключения функции в нашем проекте C++ допишем
, …
в конец всех параметров.И как Вы в таком случае будете перегружать функции? По типу, напоминаю, в php аргументы не различаются.
Эээээ…
Вам сказали: «Переменное количество аргументов в C++ тоже возможно.».
Вы попытались возразить: «там оно задается специальным синтаксисом, а в php все функции по умолчанию могут принимать переменное количество аргументов».
Я всего лишь указал на то, что в С++ гипотетически ничто не мешает создать программу, в которой все функции тоже будут принимать переменное количество аргументов. Поэтому ваш аргумент не работает (в общем случае).
Перегрузка функций в этом контексте вообще ни при чем.
Вам сказали: «Переменное количество аргументов в C++ тоже возможно.».
Вы попытались возразить: «там оно задается специальным синтаксисом, а в php все функции по умолчанию могут принимать переменное количество аргументов».
Я всего лишь указал на то, что в С++ гипотетически ничто не мешает создать программу, в которой все функции тоже будут принимать переменное количество аргументов. Поэтому ваш аргумент не работает (в общем случае).
Перегрузка функций в этом контексте вообще ни при чем.
В C++ Вы можете объявить функцию, которая не будет принимать переменное количество аргументов. Ее можно будет перегрузить (Забудем пока о перегрузке по типам аргументов).
В php Вы не сможете объявить такую функцию, которая могла бы быть перегруженной. И не потому, что перегрузки нет, а потому, что ей неоткуда взяться. Т.к. разницы между аргументами нет (sic!), а функции по умолчанию принимают произвольное (с некоторыми ограничениями, но не суть) количество аргументов, то и пространства для перегрузки попросту нет!
Про перегрузку функций — см. мое первое сообщений данной ветки.
В php Вы не сможете объявить такую функцию, которая могла бы быть перегруженной. И не потому, что перегрузки нет, а потому, что ей неоткуда взяться. Т.к. разницы между аргументами нет (sic!), а функции по умолчанию принимают произвольное (с некоторыми ограничениями, но не суть) количество аргументов, то и пространства для перегрузки попросту нет!
Про перегрузку функций — см. мое первое сообщений данной ветки.
OMG. Научитесь читать (и, желательно, понимать) комментарии, на которые пытаетесь отвечать.
Я знаю про перегрузку. Я. Просто. Указал. Вам. На. То. Что. Ваш. Аргумент. Негоден.
Я знаю про перегрузку. Я. Просто. Указал. Вам. На. То. Что. Ваш. Аргумент. Негоден.
Ну вот, я попытался прочитать всю ветку и выделить наиболее важные моменты. Но что-то я так и не понял, что Вы имеете в виду. ЧЯДНТ?
firexel спросил, почему перегрузки нет в php.
theRavel указал ему 2 причины.
barmaley_exe (То есть я) сказал, что необязательные параметры есть и в плюсах, где перегрузка есть. Но вот в тех же плюсах функции далеко не всегда (в отличие от php, где у Вас нет никакого выбора) могут принимать произвольное число аргументов.
matiouchkine (То есть Вы) говорит, что в C++ можно объявить все функции принимающими произвольное количество аргументов. К чему это вообще — непонятно. Я специально уточнил в ответе на комментарий Disturbed, что возможность перегрузить функцию по количеству аргументов (а по их типу мы не можем) определяется программистом, а не создателями языка. То есть мы можем написать в C++ функцию, которую можно перегрузить по количеству принимаемых параметров. То, что мы можем написать функцию, которую нельзя перегрузить по тому же критерию, не делает перегрузку вообще невозможным.
У barmaley_exe (то есть меня) возникает законный вопрос: как в таком случае перегружать функции по количеству аргументов (Когда они все принимают произвольное количество аргументов)? Сама концепция перегрузки неприменима в таком случае. Невозможность применения перегрузки является доказательством моего аргумента.
matiouchkine (то есть Вы) говорит, что из существования примера с нужными свойствами следует неприменимость моего аргумента. Напоминаю: разговор у нас идет о причинах отсутствия перегрузки функций в php. В качестве аргумента я назвал произвольное количество аргументов функции как причину невозможности перегрузки функций. Как возможность создания программы в C++ с произвольным количеством аргументов сказывается на перегрузке — опять же, неясно.
barmaley_exe (то есть я) снова намекает о главной мысли своих постов: в php перегрузке функций попросту неоткуда взяться.
А в ответ ему советуют научиться читать. wtf?
firexel спросил, почему перегрузки нет в php.
theRavel указал ему 2 причины.
barmaley_exe (То есть я) сказал, что необязательные параметры есть и в плюсах, где перегрузка есть. Но вот в тех же плюсах функции далеко не всегда (в отличие от php, где у Вас нет никакого выбора) могут принимать произвольное число аргументов.
matiouchkine (То есть Вы) говорит, что в C++ можно объявить все функции принимающими произвольное количество аргументов. К чему это вообще — непонятно. Я специально уточнил в ответе на комментарий Disturbed, что возможность перегрузить функцию по количеству аргументов (а по их типу мы не можем) определяется программистом, а не создателями языка. То есть мы можем написать в C++ функцию, которую можно перегрузить по количеству принимаемых параметров. То, что мы можем написать функцию, которую нельзя перегрузить по тому же критерию, не делает перегрузку вообще невозможным.
У barmaley_exe (то есть меня) возникает законный вопрос: как в таком случае перегружать функции по количеству аргументов (Когда они все принимают произвольное количество аргументов)? Сама концепция перегрузки неприменима в таком случае. Невозможность применения перегрузки является доказательством моего аргумента.
matiouchkine (то есть Вы) говорит, что из существования примера с нужными свойствами следует неприменимость моего аргумента. Напоминаю: разговор у нас идет о причинах отсутствия перегрузки функций в php. В качестве аргумента я назвал произвольное количество аргументов функции как причину невозможности перегрузки функций. Как возможность создания программы в C++ с произвольным количеством аргументов сказывается на перегрузке — опять же, неясно.
barmaley_exe (то есть я) снова намекает о главной мысли своих постов: в php перегрузке функций попросту неоткуда взяться.
А в ответ ему советуют научиться читать. wtf?
Слушайте, я не спорил с веткой. Я указал на то (третий раз повторяю, между прочим), что «там [в С++] оно задается специальным синтаксисом, а в php все функции по умолчанию могут принимать переменное количество аргументов» — плохой, негодный аргумент в силу того, что это противопоставление, которого быть не может из-за возможности сделать проект на С++, в котором все функции тоже будут принимать переменное количество аргументов.
И ровным счетом ничего более.
И ровным счетом ничего более.
Согласно Вашим утверждениям, из возможности создания проекта на C++, где все функции принимают произвольное количество аргументов, следует, что произвольное количество аргументов для любой функции не является причиной отсутствия перегрузки функций в php:
В таком случае с Вас пример, в котором функция принимает произвольное количество аргументов и может быть перегружена по количеству аргументов.
Касаемо специального синтаксиса: если Вы не укажете ..., то функция перестанет принимать произвольное число аргументов и Вы сможете перегрузить ее по количеству аргументов. А в php Вы не сможете заставить функцию принимать точное количество аргументов — ни большее, ни меньшее.
То есть в C++ Вы можете объявить функцию, которую можно будет перегрузить. В php Вы не можете жестко ограничить количество принимаемых аргументов (Возможность проверки внутри функции — это уже другое) => не можете перегрузить эту функцию по количеству аргументов.
Итого: так как в php все функции принимают произвольное количество аргументов, то, соответственно, эти функции нельзя перегрузить по количеству аргументов.
В таком случае с Вас пример, в котором функция принимает произвольное количество аргументов и может быть перегружена по количеству аргументов.
Касаемо специального синтаксиса: если Вы не укажете ..., то функция перестанет принимать произвольное число аргументов и Вы сможете перегрузить ее по количеству аргументов. А в php Вы не сможете заставить функцию принимать точное количество аргументов — ни большее, ни меньшее.
То есть в C++ Вы можете объявить функцию, которую можно будет перегрузить. В php Вы не можете жестко ограничить количество принимаемых аргументов (Возможность проверки внутри функции — это уже другое) => не можете перегрузить эту функцию по количеству аргументов.
Итого: так как в php все функции принимают произвольное количество аргументов, то, соответственно, эти функции нельзя перегрузить по количеству аргументов.
произвольное количество аргументов для любой функции не является причиной отсутствия перегрузки функций в php
В таком случае с Вас пример, в котором функция принимает произвольное количество аргументов и может быть перегружена по количеству аргументов.
«Каждая селедка рыба, но не каждая рыба селедка», — как говорил капитан Врунгель.
Прошу:
class A {
public:
virtual int a (int i, ...) {
return i;
}
virtual int a (int i1, int i2, ...) {
return i1 * i2;
}
};
#include <iostream>
using namespace std;
int main () {
A a;
cout << a.a (1) << endl;
cout << a.a (2, 2) << endl;
return 0;
}
am /tmp $ g++ a.cpp
am /tmp $ ./a.out
1
4
Хорошо, убедили.
а не является ли это «неопределённым поведением»?
усложним пример: если B является субклассом А, то какую из 2 функций должен выбрать компилятор при вызове с 2 параметрами?
virtual int a (B i, ...) {
return i;
}
virtual int a (A i1, A i2, ...) {
return i1 * i2;
}
с одной стороны первая более специфична по типу, с другой — вторая более специфична по числу параметров
усложним пример: если B является субклассом А, то какую из 2 функций должен выбрать компилятор при вызове с 2 параметрами?
virtual int a (B i, ...) {
return i;
}
virtual int a (A i1, A i2, ...) {
return i1 * i2;
}
с одной стороны первая более специфична по типу, с другой — вторая более специфична по числу параметров
Ну, немного другая модель. Зато есть необязательные параметры, можно передавать больше параметров, чем определено в функции, а так же передавать различные типы параметров, если нет type hinting'а. Все это практически компенсирует различия с Java.
ну-у перегрузку конструкторов можно «симулировать» используя call_user_func_array() snippets.dzone.com/posts/show/3237
Есть возможность вызывать функции неявно будь то метод класс или просто функция call_function
и даже можно параметры передавать как массив, а вы такое в С++ встречали или в Java, хотя я может чего-то и упускаю…
Если еще к этому добавить что ПХП не строго типизированный язык, то в нем в принципе не должно быть перегрузки. Ведь перегрузка это создание функций с идентичным именем но разными типами параметрами, при этом тип возвращаемый функцией не играет роли. Так вот в ПХП при описании функции типы не указываются и вся идея с перегрузкой — становится несуразицей.
Хотя я согласен с утверждением автора
Хотя и разработчиков ПХП понять можно. Поясню. Будем смотреть на пример автора. Внешне оба метода идентичны (совпадают и имя и параметры) и поскольку это интерфесы, то у них нет реализации и вроде бы все хорошо, но с другой стороны у нас же не строго типизированный язык, и какого типа ожидаются параметры в первом интерфесе и во втором непонятно — это потенциальная ошибка программиста. И разработчики как «багафичу» отлавливают это дело (вариант когда объявлен метод без параметров не катируется, ведь не указав параметров в объявлении мы тем самым не перекрыли возможность их передавать опять таки неявно)
Так что респект разработчикам ПХП они грамотно продумали узкие места, а если вас не устраивают интерфесы в пхп — то просто не используйте их, ведь это не необходимое условие объявления класса и т.д. Даже Страуструп в своей толстенной книге писал что только от разработчика зависит будет ли он жестко и на сколько придерживаться канонов. ПХП помогает упростит жизнь, а не усложнить…
Монолог вышел длинный, всем спасибо за терпение ;)
и даже можно параметры передавать как массив, а вы такое в С++ встречали или в Java, хотя я может чего-то и упускаю…
Если еще к этому добавить что ПХП не строго типизированный язык, то в нем в принципе не должно быть перегрузки. Ведь перегрузка это создание функций с идентичным именем но разными типами параметрами, при этом тип возвращаемый функцией не играет роли. Так вот в ПХП при описании функции типы не указываются и вся идея с перегрузкой — становится несуразицей.
Хотя я согласен с утверждением автора
Интерфейсы были придуманы как раз для того, что бы разрешить коллизию возникающую при множественном наследовании классов. Ведь если говорить грубо, то интерфейс это только «набор инструкций», который говорит, что класс имплементирующий его должен реализовать такие то методы. Интерфейс не содержит реализацию метода, по этому, классу «всё равно» если ему приходится имплементировать множество интерфейсов с одинаковыми методами.
Хотя и разработчиков ПХП понять можно. Поясню. Будем смотреть на пример автора. Внешне оба метода идентичны (совпадают и имя и параметры) и поскольку это интерфесы, то у них нет реализации и вроде бы все хорошо, но с другой стороны у нас же не строго типизированный язык, и какого типа ожидаются параметры в первом интерфесе и во втором непонятно — это потенциальная ошибка программиста. И разработчики как «багафичу» отлавливают это дело (вариант когда объявлен метод без параметров не катируется, ведь не указав параметров в объявлении мы тем самым не перекрыли возможность их передавать опять таки неявно)
Так что респект разработчикам ПХП они грамотно продумали узкие места, а если вас не устраивают интерфесы в пхп — то просто не используйте их, ведь это не необходимое условие объявления класса и т.д. Даже Страуструп в своей толстенной книге писал что только от разработчика зависит будет ли он жестко и на сколько придерживаться канонов. ПХП помогает упростит жизнь, а не усложнить…
Монолог вышел длинный, всем спасибо за терпение ;)
Вот, абсолютно правильный и очевидный ответ. Кроме того, в Java четко задается возвращаемое значение.
На мой взгляд совершенно логично запретили, если нет возможности выяснить, к какому именно интерфейсу принадлежит метод, он может быть по ошибке вызван в другом контексте — вы писали реализацию для одного интерфейса (про то, что он так же называется в другом, могли забыть/не посмотреть и т.п.), а вызван там, где требуется другой — последствия непредсказуемы.
почему тогда в Java/C# это не вызывает непредсказуемых последствий?
Может потому что там это реализовано по-другому?
Но в чем именно отличие? В том, что в C#/Java можно приводить объект класса к его интерфейсу? Ну, так и в PHP есть type hinting, который выполняет примерно ту же роль.
Я не знаю, как в С#/Java, но суть в том, что если нет механизма, позволяющего определить к какому интерфейсу принадлежит метод, у вас будут описанные проблемы. Судя по комментам выше, в этих языках используются сигнатуры методов. В Дельфи, если я окончательно не забыл, нужно явно указывать, какого интерфейса реализация. А может тоже по сигнатуре. А скорее всего комбинированно.
Я ниже описал, как это реализовано в C#.
Там по умолчанию также нельзя определить, к какому интерфейсу относится метод. Но что важно — что есть механизм, который позволяет программисту указать принадлежность явно. А в PHP реализовать такое красиво не получится, поэтому решили не реализовывать вообще никак :)
Там по умолчанию также нельзя определить, к какому интерфейсу относится метод. Но что важно — что есть механизм, который позволяет программисту указать принадлежность явно. А в PHP реализовать такое красиво не получится, поэтому решили не реализовывать вообще никак :)
только при передаче параметров в функцию. но при вызове метода никак не указать (явно через неймспейс интерфейса или приведения к типу интерфейса или неявно через тип переменной) какой интерфейс использовать.
а определять 1 метод для обоих интерфейсов нельзя, так если это разные интерфейсы, то методы имеют разную семантику, хотя и имеют одинаковые сигнатуры. интерфейс — это же не просто сигнатуры. это прежде всего набор соглашений, устанавливаемых зачастую с помощью документации, а сигнатуры — это лишь малая их часть.
а если семантика одна, то нет проблем унаследовать оба интерфейса от одного родителя
а определять 1 метод для обоих интерфейсов нельзя, так если это разные интерфейсы, то методы имеют разную семантику, хотя и имеют одинаковые сигнатуры. интерфейс — это же не просто сигнатуры. это прежде всего набор соглашений, устанавливаемых зачастую с помощью документации, а сигнатуры — это лишь малая их часть.
а если семантика одна, то нет проблем унаследовать оба интерфейса от одного родителя
конечно, просто на мой субъективный взгляд, поведение интерфейсов в php не совсем логично/отличается от ожидаемого
В них вы можете указать методы с одинаковым именем, но для разных наборов параметров. Выше уже писали.
Если вам нужно, чтобы в двух интерфейсах присутствовали методы с одинаковыми именами, и эти методы действительно семантически идентичны (т.е. у них одно и то же предназначение и одинаковый способ использования) — то выделите этот метод в отдельный интерфейс:
interface BaseInterface
{
public function testMethod();
}
interface TestInterface extends BaseInterface
{
}
interface TestInterface2 extends BaseInterface
{
}
class TestClass implements TestInterface, TestInterface2
{
public function testMethod()
{
}
}
Это скорее костыль, т.к. если мы имеем две библиотеки от разных разработчиков, которые имеют разные интерфейсы с одинаковым методом, то нам придется извращаться, вместо того, чтобы просто использовать их. Это сводит на нет преимущества интерфейсов — возможность их множественного наследования, т.к. у нас уже нет гарантии, что все будет работать.
Overloading в PHP иногда можно организовать, но через жуткие костыли. Например, через магический метод, который смотрит на количество и качество переданных параметров, и вызывает внутреннюю функцию или передаёт управление одному из нескольких подставных объектов с одноимёнными методами.
Но данную проблему это не решит, т.к. fatal error возникнет при попытке объявить класс, даже если определение функции отсутсвует.
Но данную проблему это не решит, т.к. fatal error возникнет при попытке объявить класс, даже если определение функции отсутсвует.
habrahabr.ru/blogs/php/116916/#comment_3808782 например можно реализовать overloading конструкторов через call_user_func_array()
На мой взгляд, в PHP просто не придумали хорошего способа реализовывать методы отдельно для каждого интерфейса. В C#, к примеру, для этого есть explicit interface implementation. Тогда можно сделать примерно так:
На выходе получим:
Методы, реализованные явно для какого-либо из интерфейсов, будут вызваны только при явном приведении объекта к соответствующему интерфейсу.
В PHP этого можно добиться только при использовании type hinting. Но это, очевидно, не всегда удобно.
namespace ExplicitInterfaceImplementation
{
interface ITest1
{
void TestMethod( );
}
interface ITest2
{
void TestMethod( );
}
class Test : ITest1, ITest2
{
void ITest1.TestMethod( )
{
Console.WriteLine( "ITest1" );
}
void ITest2.TestMethod( )
{
Console.WriteLine( "ITest2" );
}
public void TestMethod( )
{
Console.WriteLine( "Just test" );
}
}
class Program
{
static void Main( string[] args )
{
Test t = new Test();
t.TestMethod();
((ITest1)t).TestMethod();
((ITest2)t).TestMethod();
}
}
}
На выходе получим:
Just test
ITest1
ITest2
Методы, реализованные явно для какого-либо из интерфейсов, будут вызваны только при явном приведении объекта к соответствующему интерфейсу.
В PHP этого можно добиться только при использовании type hinting. Но это, очевидно, не всегда удобно.
Я имел в виду, что если бы в PHP было реализовано explicit interface implementation, то использовать его можно было бы только в совокупности с type hinting, что было бы не очень удобно.
В то же время, подразумевать, что для всех интерфейсов в любом классе всегда существует только одна реализация метода (даже для методов интерфейсов с разной сигнатурой) — также не очень логично и грозит ошибками.
Поэтому, наверное, волевым решением решили вообще запретить наследование от интерфейсов с одинаковыми именами методов :)
В то же время, подразумевать, что для всех интерфейсов в любом классе всегда существует только одна реализация метода (даже для методов интерфейсов с разной сигнатурой) — также не очень логично и грозит ошибками.
Поэтому, наверное, волевым решением решили вообще запретить наследование от интерфейсов с одинаковыми именами методов :)
abstract class c1 {
abstract public function m1();
}
abstract class c2 extends c1 {
abstract public function m1();
}
Эта правда имеет меньше смысла, но суть таже…
PHP Fatal error: Can't inherit abstract function c1::m1() (previously declared abstract in c2)
PS: Почему у меня не работает подсветка кода? :(
Тег source lang=«php» написал…
abstract public function m1();
}
abstract class c2 extends c1 {
abstract public function m1();
}
Эта правда имеет меньше смысла, но суть таже…
PHP Fatal error: Can't inherit abstract function c1::m1() (previously declared abstract in c2)
PS: Почему у меня не работает подсветка кода? :(
Тег source lang=«php» написал…
Простите, а какое практическое применение может быть у вашего примера? Проблему автора я понимаю, но это…
Меня опять будут минусовать, но в Java это работает без проблем. Фактически это простое наследование и так сказать en.wikipedia.org/wiki/Method_overriding
public abstract class someClass {
abstract public void method();
}
public abstract class anotherClass extends someClass {
abstract public void method();
}
public class main extends anotherClass {
/**
* @param args
*/
public static void main(String[] args) {
main main = new main();
main.method();
}
@Override
public void method() {
System.out.println("method() was called");
}
}
Автор оригинального комментария, так и не смог сказать, зачем оно нужно на практике. Может быть Вы сможете? То, что оно работает в Java — это не показатель.
Для увеличения очевидности и наглядности кода. Не надо бежать по иерархии классов вверх, чтобы посмотреть, какие методы есть у класса.
Хм, я думал, в современных IDE с этим проблем нет. Я был неправ?
А они покажут Вам при взгляде на объявление абстрактного класса, унаследованного от кого-нибудь, что он наследует? Я что-то сомневаюсь.
Ну и не все пользуются умными и навороченными IDE. Более того, случается так, что код просто смотрят, не редактируя (ну захотелось мне посмотреть, как %projectname% изнутри устроен). В таких случаях разворачивать его в IDE — нерационально, я считаю.
Но в целом, да — с учетом наличия умных IDE это не так критично.
Ну и не все пользуются умными и навороченными IDE. Более того, случается так, что код просто смотрят, не редактируя (ну захотелось мне посмотреть, как %projectname% изнутри устроен). В таких случаях разворачивать его в IDE — нерационально, я считаю.
Но в целом, да — с учетом наличия умных IDE это не так критично.
вполне возможно в каких-то больших библиотеках с сложной иерархией.
Я вообще не понимаю что же тут можно не понять в этой ситуации.
Что такое интерфейс? Набор методов, которые класс должен реализовать, чтобы объект мог выступать в какой-то роли. В каждой роли — свой набор методов.
Теперь предположим, что в двух разных интерфейсах требуется реализовать один метод. Как же понять в какой роли дёргается метод класса?
При вызове next, которых описан сразу в каких-нибудь гипотетических «XMLIterator» и «ArrayIterator» какой из указателей переместить на следующую позицию? Указатель в дереве XML, доступ к которому предоставляет наш объект? Указатель в неком массиве, доступ к которому так же предоставляет нашим объектом?
Что такое интерфейс? Набор методов, которые класс должен реализовать, чтобы объект мог выступать в какой-то роли. В каждой роли — свой набор методов.
Теперь предположим, что в двух разных интерфейсах требуется реализовать один метод. Как же понять в какой роли дёргается метод класса?
При вызове next, которых описан сразу в каких-нибудь гипотетических «XMLIterator» и «ArrayIterator» какой из указателей переместить на следующую позицию? Указатель в дереве XML, доступ к которому предоставляет наш объект? Указатель в неком массиве, доступ к которому так же предоставляет нашим объектом?
Понять очень просто.
public function doSomething1(someInterface $vasia) {
$vasia->someMethod();
}
public function doSomething2(anotherInterface $vasia) {
$vasia->someMethod();
}
Совершенно очевидно, что в первом случае вызывается метод someInterface::someMethod, а во втором — anotherInterface::someMethod.
Такие вещи прекрасно продуманы в очень хорошем языке Borland Pascal (он же Object Pascal, он же Delphi). Помню, ещё много лет назад восхищался как раз этим моментом. А года полтора назад ругался на PHP, потому что в нём такое сделать нельзя.
public function doSomething1(someInterface $vasia) {
$vasia->someMethod();
}
public function doSomething2(anotherInterface $vasia) {
$vasia->someMethod();
}
Совершенно очевидно, что в первом случае вызывается метод someInterface::someMethod, а во втором — anotherInterface::someMethod.
Такие вещи прекрасно продуманы в очень хорошем языке Borland Pascal (он же Object Pascal, он же Delphi). Помню, ещё много лет назад восхищался как раз этим моментом. А года полтора назад ругался на PHP, потому что в нём такое сделать нельзя.
А какой метод вызывать у объекта $vasia, например, при вызове someMethod() вне всяких методов функций?
?!
<?php
$vasia = new Foo();
$vasia->someMethod();
?!
очень хороший пример за что мне нравится реализация ООП в php. В данной задаче дублирование, которое нужно вынести в отдельный интерфейс.
но в целом у php есть одна большая проблема — анализ и компиляция в опкод должна проходить максимально быстро. поэтому разработчикам приходится искать баланс между фичами и скоростью. синтаксический сахар возможен только если кешеры опкода будут в ядре, тогда можно будет развернуться и сделать разбор скриптов более интеллектуальным, добавить оптимизаторы и получить java ;).
но в целом у php есть одна большая проблема — анализ и компиляция в опкод должна проходить максимально быстро. поэтому разработчикам приходится искать баланс между фичами и скоростью. синтаксический сахар возможен только если кешеры опкода будут в ядре, тогда можно будет развернуться и сделать разбор скриптов более интеллектуальным, добавить оптимизаторы и получить java ;).
Не согласен с вами, не всегда возможно вынести методы в отдельный интерфейс, особенно когда работаешь с чужим кодом.
Вы всё спорите, а посмотрели бы в исходники. Интерфейс во внутреннем представлении — обычный класс. Все правила обычного класса относятся к интерфейсу.
Мне кажется, девелоперам просто было лень усложнять парсер дополнительным функционалом, так как для определения интерфейса используются те же функции парсера, что и для обычного класса.
Мне кажется, девелоперам просто было лень усложнять парсер дополнительным функционалом, так как для определения интерфейса используются те же функции парсера, что и для обычного класса.
А если посмотреть с другой стороны, одни программист написал интерфейс подразумевая одно, другой написал такой же интерфейс подразумевая совсем другое и скажем комментарии оставил отличный от комментарии первого. Так и запутаться можно в том, что хотел один сказать, а что сказал другой! Но… у каждого есть твоё мнение )
Не минимизировали проблему… Один программист написал код, а другой программист не знал, что это, но решил запустить. Мне кажется, защиту от этого стоит срочно встроить во все языки.
Мне кажется, проблема надуманная. Если два интерфейса обладают одним и тем же методом, который служит для одного и того же, то этот метод скорее всего должен быть унаследован от некоего абстрактного интерфейса по логике вещей. В противном случае такого просто быть не должно, если серьезно подходить к именованию методов, т.е. чтобы название метода однозначно говорило, что он делает.
Надо отметить, что в 5.4 у traits будет возможность разрешать конфликты и переименовывать методы; но надо понимать, что trait — это не интерфейс, задачи у этих механизмов разные.
Надо отметить, что в 5.4 у traits будет возможность разрешать конфликты и переименовывать методы; но надо понимать, что trait — это не интерфейс, задачи у этих механизмов разные.
>если серьезно подходить к именованию методов
Есть нюанс — не все интерфейсы под контролем разработчика, например при использовании внешних библиотек.
Есть нюанс — не все интерфейсы под контролем разработчика, например при использовании внешних библиотек.
У меня не получается представить ситуацию, в которой мне пришлось бы реализовывать в одном классе два сторонних интерфейса. Можете привести более-менее реалистичный пример?
www.php.net/manual/en/class.arrayiterator.php
www.php.net/manual/en/class.arrayobject.php
AppendIterator extends IteratorIterator implements OuterIterator , Traversable , Iterator {
www.php.net/manual/en/class.arrayobject.php
ArrayObject implements IteratorAggregate , Traversable , ArrayAccess , Serializable , Countable {
Например, какой-то объект (пускай логгер) должен быть подписан на события от двух разных библиотек. Обе предполагают, что наблюдатель должен реализовывать их интерфейсы, реализующий, в частности, метод handleEventт. Была бы возможность адресовать конкретные интерфейсы, можно было бы обойтись одним классом, без неё придётся вводить прокси-классы чуть ли не из одной строки.
Для интеграции несовместимых интерфейсов существует паттерн "Адаптер". А объект-логгер должен быть контейнером для объектов-адаптеров с обобщенным интерфейсом. Даже в отсутствие конфликта интерфейсов нормальный проектировщик поступит именно так в целях масштабируемости. «Минимизация количества классов» — не та цель, за которую стоит воевать. Классов должно быть столько, сколько необходимо для реализации грамотной архитектуры.
Я считаю, что если возникает проблема «конфликтующих интерфейсов», то это явный результат грязных хаков и плохой архитектуры.
Я считаю, что если возникает проблема «конфликтующих интерфейсов», то это явный результат грязных хаков и плохой архитектуры.
Его и имел в виду под «прокси-классы», с теорией у меня плохо, вот даже как-то дышу, но не знаю как :( Но вообще-то даже из вашей ссылки видно, что назначение адаптера не в этом, он предназначен для использования недоступного для модификации объекта, а у в моём примере ситуация ровно наоборот — недоступные для модификации объекты должны использовать объект разрабатываемый.
Ну и концептуальной разницы не вижу между реализацией нескольких интерфейсов в одном классе (класс выступает контейнером для интерфейсов) или в нескольких, один из которых выступает контейнером для остальных. Да и использовать его проще, по-моему, было бы при нормальных множественных интерфейсах, например
Разве что можно придраться, что у моего обозревателя на более детализированном уровне возникает несколько ответственностей — основная и преобразование от внешних интерфейсов к внутренним (или, на практике скорее, от одного внешнего к другому), но если преобразование примитивно (изменение сигнатуры для одного-двух методов) и число интерфейсов невелико, то введение адаптеров, по-моему, только усложнит логику приложения для понимания (увеличит количество классов и связей между ними в том числе), увеличит количество кода, снизит его эффективность и всё это без очевидного профита.
Конечно, если число интерфейсов и методов в них велико, преобразования не тривиальны, то вводить адаптеры нужно, прежде всего, для облегчения понимания кода, чтобы за деревьями (сложные преобразование и/или их большое количество) увидеть лес (основная функция). Но когда лес и так видно, да и не лес, а так, дуб (основная функция) и три кустика (небольшое количество примитивных преобразований), то пересаживать кустики от дуба подальше может быть излишним, по-моему. Вот начнут кустики разрастаться (увеличится количество преобразований), расти в высоту (усложнятся), два кустика нужно будет одинаковых (конфликт имён) или сам дуб начнёт расти (основная функция объекта увеличится), в общем количество перейдёт в качество — можно и нужно пересадить (сделать рефакторинг), а предусматривать любые возможные будущие изменения, по-моему, сродни преждевременной оптимизации — зачем решать проблему, которая может возникнуть, а может и не возникнуть?
Ну и концептуальной разницы не вижу между реализацией нескольких интерфейсов в одном классе (класс выступает контейнером для интерфейсов) или в нескольких, один из которых выступает контейнером для остальных. Да и использовать его проще, по-моему, было бы при нормальных множественных интерфейсах, например
$observable1->setObserver((IObserver1)$observer); //приведение для наглядности по идее
$observable2->setObserver((IObserver2)$observer);
проще, чем$observable1->setObserver($observer->getObserver1Adapter());
$observable2->setObserver($observer->getObserver2Adapter());
или$observable1->setObserver(new Observer1Adapter($observer);
$observable2->setObserver(new Observer2Adapter($observer);
Разве что можно придраться, что у моего обозревателя на более детализированном уровне возникает несколько ответственностей — основная и преобразование от внешних интерфейсов к внутренним (или, на практике скорее, от одного внешнего к другому), но если преобразование примитивно (изменение сигнатуры для одного-двух методов) и число интерфейсов невелико, то введение адаптеров, по-моему, только усложнит логику приложения для понимания (увеличит количество классов и связей между ними в том числе), увеличит количество кода, снизит его эффективность и всё это без очевидного профита.
Конечно, если число интерфейсов и методов в них велико, преобразования не тривиальны, то вводить адаптеры нужно, прежде всего, для облегчения понимания кода, чтобы за деревьями (сложные преобразование и/или их большое количество) увидеть лес (основная функция). Но когда лес и так видно, да и не лес, а так, дуб (основная функция) и три кустика (небольшое количество примитивных преобразований), то пересаживать кустики от дуба подальше может быть излишним, по-моему. Вот начнут кустики разрастаться (увеличится количество преобразований), расти в высоту (усложнятся), два кустика нужно будет одинаковых (конфликт имён) или сам дуб начнёт расти (основная функция объекта увеличится), в общем количество перейдёт в качество — можно и нужно пересадить (сделать рефакторинг), а предусматривать любые возможные будущие изменения, по-моему, сродни преждевременной оптимизации — зачем решать проблему, которая может возникнуть, а может и не возникнуть?
А зачем вообще в динамическом языке интерфейсы?
То, что язык динамический, не значит, что нельзя организовать проверку на тип. В PHP для этого есть Type Hinting. Можно привести объект к какому-то интерфейсу и работать с ним исключительно через этот интерфейс.
Я не о принципиальной возможности. А о том зачем это могло бы быть нужно практически? Потому что «во-первых, это красиво»? :)
Какие задачи, не решаемые или трудно решаемые без интерфейсов, они позволяют решать? Или это из серии уродских долларов перед переменной — чтобы программист, цуко, не забыл что это переменная (а из контекста не видно?).
По-моему, как в случае с долларами — создатель языка то ли не понял зачем они нужны были в пёрле, то ли не осилил написать парсер без долларов, так и здесь — пёрли из явы ООП модель, и зачем-то до кучи прихватили и интерфейсы — «шоб було» :).
Какие задачи, не решаемые или трудно решаемые без интерфейсов, они позволяют решать? Или это из серии уродских долларов перед переменной — чтобы программист, цуко, не забыл что это переменная (а из контекста не видно?).
По-моему, как в случае с долларами — создатель языка то ли не понял зачем они нужны были в пёрле, то ли не осилил написать парсер без долларов, так и здесь — пёрли из явы ООП модель, и зачем-то до кучи прихватили и интерфейсы — «шоб було» :).
Вот, к примеру, в PHP есть стандартные интерфесы: Serializable и Countable. Вполне логично, что эти два поведения класса никак не связаны, следовательно, любой класс может реализовывать как одно из них, так и оба.
Какой вы видите другой способ реализации такой возможности? Проверка через reflection на наличие методов вида __sleep() и __wakeup() (раньше именно объявляя эти методы класс заявлял, что его объекты можно сериализовать)? Но ведь с интерфейсами намного удобнее и красивее!
Какой вы видите другой способ реализации такой возможности? Проверка через reflection на наличие методов вида __sleep() и __wakeup() (раньше именно объявляя эти методы класс заявлял, что его объекты можно сериализовать)? Но ведь с интерфейсами намного удобнее и красивее!
>Какой вы видите другой способ реализации такой возможности?
Примесями? :) Просто реализация соответствующих методов не подходит?
>Проверка через reflection на наличие методов вида __sleep() и __wakeup()
В каком смысле проверка? Кто проверяет? Я не помню как устроена рефлексия в пхп, если как в яве, то до, это чрезмерная сложность. Но в яве понятно почему так, а зачем так в пхп? Опять тянули по принципу «чтоб було»? А если как в руби, то отчего не проверить, раз это легко и просто? И если уж очень захотелось проверить. А если проверять не захотелось, то какая разница какая эксепция прилетит — «не реализован интерфейс» или «отсутствует такой метод»?
>Но ведь с интерфейсами намного удобнее и красивее!
Ок. Я не до конца осознал зачем это нужно, но, похоже, для борьбы с остальной кривостью :). Тоже вариант :) :).
Примесями? :) Просто реализация соответствующих методов не подходит?
>Проверка через reflection на наличие методов вида __sleep() и __wakeup()
В каком смысле проверка? Кто проверяет? Я не помню как устроена рефлексия в пхп, если как в яве, то до, это чрезмерная сложность. Но в яве понятно почему так, а зачем так в пхп? Опять тянули по принципу «чтоб було»? А если как в руби, то отчего не проверить, раз это легко и просто? И если уж очень захотелось проверить. А если проверять не захотелось, то какая разница какая эксепция прилетит — «не реализован интерфейс» или «отсутствует такой метод»?
>Но ведь с интерфейсами намного удобнее и красивее!
Ок. Я не до конца осознал зачем это нужно, но, похоже, для борьбы с остальной кривостью :). Тоже вариант :) :).
На самом деле, даже наличие методов с определенными именами не гарантирует на 100%, что объект предоставляет семантически подходящий контракт. Например, есть у вас метод Calc() в объекте, и что дальше? Или, может быть, нужно называть методы сложными именами, которые вряд ли пересекутся с именами методов в других объектах?
А если в нужном интерфейсе дложно быть 10 методов, что делать? Проверять на наличие всех 10-ти?
На мой взгляд, интерфейсы важны как раз потому, что позволяют задавать семантику использования объекта.
А если в нужном интерфейсе дложно быть 10 методов, что делать? Проверять на наличие всех 10-ти?
На мой взгляд, интерфейсы важны как раз потому, что позволяют задавать семантику использования объекта.
>На самом деле, даже наличие методов с определенными именами не гарантирует на 100%, что объект предоставляет семантически подходящий контракт.
Ну да. И что? Либо вы верите документации, либо лезете в код, либо пишите всё сами. А, да, в какой-то степени может помочь юнит-тестирование. Но куда тут приделать интерфейсы?..
>Проверять на наличие всех 10-ти?
Я так до конца и не понял насчёт этого «проверять». Зачем проверять? Кому проверять? Как вы проверяете семантику упомянутого выше метода Calc()? Как вы проверяете реализованы ли в классе все методы объявленного интерфейса?
Ну да. И что? Либо вы верите документации, либо лезете в код, либо пишите всё сами. А, да, в какой-то степени может помочь юнит-тестирование. Но куда тут приделать интерфейсы?..
>Проверять на наличие всех 10-ти?
Я так до конца и не понял насчёт этого «проверять». Зачем проверять? Кому проверять? Как вы проверяете семантику упомянутого выше метода Calc()? Как вы проверяете реализованы ли в классе все методы объявленного интерфейса?
Честно говоря, я не вижу, как мне выразиться яснее. Интерфейсы нужны для того, чтобы задать контракт на уровне сигнатуры класса. Без них вы задаете только сигнатуры отдельных методов. Это просто более высокоуровневая структура любого API.
С тем же успехом можно спорить о том, что классы не нужны, ведь можно обойтись отдельными переменными или массивами. Да, можно, но это не так удобно.
С тем же успехом можно спорить о том, что классы не нужны, ведь можно обойтись отдельными переменными или массивами. Да, можно, но это не так удобно.
>Интерфейсы нужны для того, чтобы задать контракт на уровне сигнатуры класса
Угу. Но в данном случае они выполняют чисто декоративные (документирующие) функции.
>С тем же успехом можно спорить о том, что классы не нужны, ведь можно обойтись отдельными переменными или массивами
Тут немного не то. Классы добавляют некоторое новое качество. Даже три :).
Угу. Но в данном случае они выполняют чисто декоративные (документирующие) функции.
>С тем же успехом можно спорить о том, что классы не нужны, ведь можно обойтись отдельными переменными или массивами
Тут немного не то. Классы добавляют некоторое новое качество. Даже три :).
>Я не помню как устроена рефлексия в пхп, если как в яве, то до, это чрезмерная сложность.
Ближе к Яве рефлексия реализована
>А если проверять не захотелось, то какая разница какая эксепция прилетит — «не реализован интерфейс» или «отсутствует такой метод»?
Вот как раз в Яве непонятно какая разница какая ошибка вылетит во время компиляции :) В ПХП же какой-нибудь метод может не вызываться годами (какой-нибудь очень редкий юз-кэйс или, например, ошибки внешних сервисов) и годами может никто не догадываться, что метода-то и нет, а так получим ошибку, при загрузке класса, когда ещё, может быть, ни одна строчка кода логики не выполнена.
Ближе к Яве рефлексия реализована
>А если проверять не захотелось, то какая разница какая эксепция прилетит — «не реализован интерфейс» или «отсутствует такой метод»?
Вот как раз в Яве непонятно какая разница какая ошибка вылетит во время компиляции :) В ПХП же какой-нибудь метод может не вызываться годами (какой-нибудь очень редкий юз-кэйс или, например, ошибки внешних сервисов) и годами может никто не догадываться, что метода-то и нет, а так получим ошибку, при загрузке класса, когда ещё, может быть, ни одна строчка кода логики не выполнена.
Да, в статически типизированных, компилируемых языках интерфейсы и предназначены втч для того, чтобы осуществить контроль на стадии компиляции. Но мы о динамических.
В описанном вами случае ровно с той же степенью вероятности и соответствующий файл не будет подружаться теми же самыми годами, таким образом возвращаемся на исходную :). Как раз в этом случае рулят юнит тесты.
В общем и целом, я, конечно же, понял вашу и другого моего собеседника точку зрения, не, по прежнему, её не разделяю. По-моему, это неудачное использование не нужного в данном случае инстумента и неудобств от него больше, чем пользы.
В описанном вами случае ровно с той же степенью вероятности и соответствующий файл не будет подружаться теми же самыми годами, таким образом возвращаемся на исходную :). Как раз в этом случае рулят юнит тесты.
В общем и целом, я, конечно же, понял вашу и другого моего собеседника точку зрения, не, по прежнему, её не разделяю. По-моему, это неудачное использование не нужного в данном случае инстумента и неудобств от него больше, чем пользы.
Всё же вероятность того, что класс не будет загружаться поменьше, если интерфейс не из одного метода :) Включая использование юнит-тестов — не нужно 100% покрытие класса тестами, достаточно хотя бы одного формального теста, вернее даже одного инклуда в тесте.
На самом деле тут PHP, по-моему, удачно комбинирует достоинства статических и динамических языков. Не хотите контролировать на этапе «компиляции» (или даже разработки — IDE подскажет) некоторые типы данных (включая интерфейсы) — не контролируйте, хотите — контролируйте. Иметь возможность хоть как-то реализовать множественное наследование лучше, чем не иметь её вовсе. Главное не злоупотреблять предоставленными возможностями.
На самом деле тут PHP, по-моему, удачно комбинирует достоинства статических и динамических языков. Не хотите контролировать на этапе «компиляции» (или даже разработки — IDE подскажет) некоторые типы данных (включая интерфейсы) — не контролируйте, хотите — контролируйте. Иметь возможность хоть как-то реализовать множественное наследование лучше, чем не иметь её вовсе. Главное не злоупотреблять предоставленными возможностями.
Почему никто не указал, что PHP слаботипизированный язык.
Поэтому нельзя полноценно указать, какой из типов данных будет присутствовать внутри функции
Это и есть основное различие с типизированными языками, такими как Java или C#. И эта особенность и легла в основу ограничений языка PHP
Поэтому разработчики PHP и не создавали возможность overload(override) для этого языка, потому что неизвестно какой именно параметр и какого именно типа будет в сигнатуре метода
Так как нет возможности перегрузки метода, соотвественно нет и возможности наследования нескольких интерфейсов с одинаковыми именами методов.
Поэтому нельзя полноценно указать, какой из типов данных будет присутствовать внутри функции
Это и есть основное различие с типизированными языками, такими как Java или C#. И эта особенность и легла в основу ограничений языка PHP
Поэтому разработчики PHP и не создавали возможность overload(override) для этого языка, потому что неизвестно какой именно параметр и какого именно типа будет в сигнатуре метода
Так как нет возможности перегрузки метода, соотвественно нет и возможности наследования нескольких интерфейсов с одинаковыми именами методов.
Да, именно из-за динамичности в PHP методы нельзя overload'ить. В частности, из-за того, что любой метод по умолчанию может принимать разное количество параметров.
Однако же, указать, какого типа должен быть каждый параметр метода, все-таки можно — при помощи Type Hinting. Так что с этой точки зрения все в порядке.
>> Так как нет возможности перегрузки метода, соотвественно нет и возможности наследования нескольких интерфейсов с одинаковыми именами методов.
А вот к наследованию интерфейсов с одинаковыми именами методов наличие/отсутствие возможности перегрузки методов не имеет отношения. Ведь в других языках можно наследоваться от нескольких интерфейсов, даже если в них есть методы с абсолютно идентичной сигнатурой. В C#, к примеру, нужно будет реализовать этот метод один раз и эта реализация будет использоваться при вызове метода через любой из интерфейсов.
Вопрос в том, чтобы предоставить разрабточику механизм, который позволяет разделять реализации метода в зависимости от интерфейса, через который он вызывается. Как это реализовано в C#, я описал вот в этом комментарии. В PHP такое реализовать было возможно, но далеко не всегда удобно, т.к. Type Hinting можно применять только при вызове методов.
Видимо, не найдя красивого и универсального решения, решили вообще запретить имплементацию конфликтующих интерфейсов.
Однако же, указать, какого типа должен быть каждый параметр метода, все-таки можно — при помощи Type Hinting. Так что с этой точки зрения все в порядке.
>> Так как нет возможности перегрузки метода, соотвественно нет и возможности наследования нескольких интерфейсов с одинаковыми именами методов.
А вот к наследованию интерфейсов с одинаковыми именами методов наличие/отсутствие возможности перегрузки методов не имеет отношения. Ведь в других языках можно наследоваться от нескольких интерфейсов, даже если в них есть методы с абсолютно идентичной сигнатурой. В C#, к примеру, нужно будет реализовать этот метод один раз и эта реализация будет использоваться при вызове метода через любой из интерфейсов.
Вопрос в том, чтобы предоставить разрабточику механизм, который позволяет разделять реализации метода в зависимости от интерфейса, через который он вызывается. Как это реализовано в C#, я описал вот в этом комментарии. В PHP такое реализовать было возможно, но далеко не всегда удобно, т.к. Type Hinting можно применять только при вызове методов.
Видимо, не найдя красивого и универсального решения, решили вообще запретить имплементацию конфликтующих интерфейсов.
Гуру С++, в частности Скот Майерс, не советуют вообще использовать множественное наследование (Возможно у Александреску тоже есть пара слов об этом… ).
Так что не советую использовать множественное наследование и в РНР.
Так что не советую использовать множественное наследование и в РНР.
Sign up to leave a comment.
Тонкости (странности?) работы с интефейсами