Closed marc-mabe closed 9 years ago
@0xPaul the final
keyword doesn't help in this case because the enumeration class itself doesn't know the implementator. For example an HttpMethodEnum
defines standard HTTP methods like GET
, POST
... and it will be extended by WebDavMethodEnum
. This will be very valid but a HTTP client expecting HttpMethodEnum
does or does not handle WebDavMethodEnum
. The client can type-hint HttpMethodEnum
but then all extended enumerations will pass. Using the currently recommended way to check via HttpMethodEnum::get($enum)
the client makes sure to not get an extended version but that breaks OOP inheritance - e.g. you can't overwrite __toString
method.
Another issue with inheritance is the single instance per value rule. If you have an extended version like the one described above the following produces WTF moments:
$get1 = HttpMethodEnum::get('GET');
$get2 = WebDavMethodEnum::get('GET');
var_dump($get1 === $get2); // false
var_dump($get1->is($get2)); // true
var_dump($get2->is($get1)); // false
The third comparison $get2->is($get1)
could be solved by changing the following:
final public function is($enum)
{
return $this->value === $enum || ($enum instanceof static && $this->value === $enum->getValue());
}
// to
final public function is($enum)
{
return $this->value === $enum || (($enum instanceof static || $this instanceof $enum) && $this->value === $enum->getValue());
}
I have decided to basically allow inheritance but this library will handle child enumerations as a different type.
Enum::get()
will throw an exception if a given instance doesn't match the exact called class nameEnum::is()
will return false if a given instance isn't exactly the same as this instanceEnumMap
and EnumSet
will throw an exception on attaching an instance that doesn't match the exact class name the map or set was instantiated for.Example:
class EnumA extends Enum {
const A = 'A';
}
class EnumB extends EnumA {
const B = 'B';
}
$aa = EnumA::A();
$ba = EnumB::A();
$aa === $ba; // false -> different instances
$aa->is($ba); // false -> different enumerations
$ba->is($aa); // false -> different enumerations
$aa->is($ba->getValue()); // true -> Because the enumerator of type EnumA will be checked against a matching scalar value
$ba->is($aa->getValue()); // true -> Because the enumerator of type EnumB will be checked against a matching scalar value
EnumA::get($ba); // -> InvalidArgumentException
EnumB::get($aa); // -> InvalidArgumentException
EnumA::get($ba->getValue()); // -> returns same as $aa
EnumB::get($aa->getValue()); // -> returns same as $ba
By sure inheritance works the same as for others. So type-hints will allow inherited enumeration types as well and properties of an enumeration will be inherited by well known rules. The developer has to be in mind of it.
PS: I'll create own issues to match this rules.
http://stackoverflow.com/questions/1414755/java-extend-enum https://blogs.oracle.com/darcy/entry/enums_and_mixins
In Java it's not possible to extend an enum because a class expecting a specialized enum type doesn't know about extended values.
From an internal development point of view it would be more simple to allow only enumerations extending the base class directly because no class name handling (called class) will be required.
On the side enumeration classes can have additional functionalities and are not limited to value definitions alone. In such cases it could make sense to add or overwrite functionalities and extending would be the way to go.