Closed jens1o closed 6 years ago
You can reuse Entry
with different types.
new Entry('key', new Foo()); // 1
new Entry(0, new Bar()); // 2
1 will be an Entry<string, Foo>
while 2 is an Entry<int, Bar>
Why I do not use abstract classes that can handle such types?
What classes would you create?
A class (like IntEntry
) that inherits from something like StringEntry
that is only responsible to compensate that I only have an Int
and need to fetch the data so it works as expected.
I question your data model if IntEntry
inherits from StringEntry
, but let's assume it does here. What about typing the values? Do you create two new classes for each type (int, double, string, bool, array, and every class that might want to use it)? What if a library is providing this class and doesn't know what types will be available? What if key can accept complex types? Are you creating n x m different classes to handle every possibility?
When I would not create these amount of classes, I'd need to make one huge class that accept a mixed
type and then needs to check every little thing to extract somehow the data I need?
It makes more sense for me to create a class for every thing that exists to keep my code clean.
No, using generic programming, when you do
$entry = new Entry('key', new Foo());
You're specifying that the key is a string and the value is a Foo
(through inference). Then when you call
$foo = $entry->getValue();
You know that $foo
is a Foo
.
You could also explicitly set the types, but it's not necessary.
$entry = new Entry<string, Foo>('key', new Foo());
But isn't it easier to just have dedicated classes? Otherwise, when I call getValue
I need to handle each possible return value at each position I call this method. When I just create a class that is dedicated to provide me a seamless interface, I will have a lot less code to maintain, and this idea is completely useless.
I do not see the point why I should do this.
No, as I've stated previously, you know exactly what types are available. Let's use a better example:
class Collection<T>
{
private $items = [];
public function add(T $item)
{
$this->items[] = $item;
}
public function get(int $index): T
{
return $this->items[$index];
}
}
Without generics, you could not use this class in a type safe way without creating different subclasses, as you mentioned. Although, because you have to default to any type, you'd be violating LSP if you subclassed this class and added type information.
With generics, you can have your collection hold different types of items reusing the same class.
new Collection<int>();
new Collection<string>();
new Collection<Hat>();
When using the collection, you know exactly what type of collection you're using because you type to it.
If you have a function that operates on the collection, you do not just type to Collection
, you type to the type of collection you need.
function addAHat(Collection<Hat> $hatCollection, Hat $hat): void
{
$hatCollection->add($hat);
}
Because you're typing to Collection<Hat>
, the only thing that's allowed in the collection are Hat
s. The following would fail.
function addAHat(Collection<Hat> $hatCollection, Hat $hat): void
{
$hatCollection->add(1);
}
The following would also fail
function addAHat(Collection<Hat> $hatCollection, Hat $hat): void
{
$hatCollection->add($hat);
}
addAHat(new Collection<int>(), new Hat());
You end of writing a lot less code this way because you can reuse a single class in a type safe way.
Oh. Now I got it. :D
I'm so sorry for my misunderstandings :(
The definition in line 1 is completely useless, isn't it? There already is typechecking going on in the constructor? How does this help me?