Giriş
3 çeşit variance tanımı var.
1. Covariance
Açıklaması şöyle
If S is a substype of T, then List[S] and List[T are unrelated
Nesneye Yönelik Diller (Objet Oriented Languages) özelliklerine göre sınıflandırılabiliyor. Sınıflandırmak için kullanılan iki özellik Covariance ve Contravariance. Bir dil özelliği desteklemiyorsa Intravariant olarak sınıflandırılır.
Önce covariance kavramına bakalım çünkü bu kavram da kendi içinde ayrışmış durumda.
Covariance
Covariant kalıtan sınıfın üst sınıf yerine kullanılabilmesi demek. Kavram kendi içinde covariant nesne atama ve covariant return type olarak ayrışmış durumda.
1. Covariant Nesne Atama
Bu kavram Liskov Kuralı'nın generics/template ile beraber kullanılması anlamına gelir.
C++
Maalesef C++'taki template mekanizması covariant değil. Şöyle bir kod derlenmez.
Covariant Nesne Atama yazısına taşıdım.
C#
2. Covariant Return TypeCovariant kalıtan sınıfın üst sınıf yerine kullanılabilmesi demek. Kavram kendi içinde covariant nesne atama ve covariant return type olarak ayrışmış durumda.
1. Covariant Nesne Atama
Bu kavram Liskov Kuralı'nın generics/template ile beraber kullanılması anlamına gelir.
C++
Maalesef C++'taki template mekanizması covariant değil. Şöyle bir kod derlenmez.
vector<Derived*> v1;
vector<Base*> v2;
v2 = v1; //Compiler error here
JavaCovariant Nesne Atama yazısına taşıdım.
C#
Covariant Return Type virtual bir metod A tipini döndürüyorsa, bu metodu yeniden gerçekleştiren bir alt sınıf A'dan kalıtan B tipini döndürebilir anlamına gelir.
Örnek:
abstract class Enclosure
{
public abstract Animal Contents();
}
class Aquarium : Enclosure
{
public override Fish Contents() { ... }
}
- C++ covariant return type yöntemini destekler.
- Java da desktekler. Bu özelliklik Java 1.5 ile geldi.
- C# desteklemez! Yani C# invariant'tır
Generic Covariant Return Type
Not : Burada dikkat edilmesi gereken nokta, yukarıdaki covariant örneklerinde virtual bir metod sağlayan bir ata sınıf vardı. Kalıtan sınıf ata sınıfın virtual metodunu override ederken A yerine A'dan kalıtan B nesnesini dönüyordu. Dolayısıyla C# bu tür covariant işlemleri desteklemiyor. Bu örnekte ise ata sınıf virtual bir metod sağlamıyor. Sadece dönen nesne için kalıtım var.
C# sadece generic covariant type'ları interface ve delegate için destekler. Şöyle bir getter metodu olan sınıf olsun
Contravariance
Contravariance ata sınıfın alt sınıf yerine kullanılabilmesi demek
Not : Burada dikkat edilmesi gereken nokta, yukarıdaki covariant örneklerinde virtual bir metod sağlayan bir ata sınıf vardı. Kalıtan sınıf ata sınıfın virtual metodunu override ederken A yerine A'dan kalıtan B nesnesini dönüyordu. Dolayısıyla C# bu tür covariant işlemleri desteklemiyor. Bu örnekte ise ata sınıf virtual bir metod sağlamıyor. Sadece dönen nesne için kalıtım var.
C# sadece generic covariant type'ları interface ve delegate için destekler. Şöyle bir getter metodu olan sınıf olsun
public DashboardNotification<IDashboardEntry> Get()
{
return new MyWorkingNotification();
}
Bu sınıf MyWorkingNotification nesnesini dönebilir çünkü sınıf şöyle tanımlıpublic class MyNotWorkingNotification : DashboardNotification<MyDashboardEntry>
{
}
Şu nesneyi ise dönemeyiz. Derleyici hata verir.public class MyNotWorkingNotification : DashboardNotification<MyDashboardEntry>
{
}
Contravariance
Contravariance ata sınıfın alt sınıf yerine kullanılabilmesi demek
Contravariant Method Parameter
Virtual bir metodun parametresi A tipinden bir nesne alıyorsa, bu metodu gerçekleştiren bir başka metod imzasında A'dan kalıtan B tipini kullanabilir. C++, Java ve C# contravariant method parameter yöntemini desteklemezler! Ancak Generic kulllanarak bir nebze bu işlem yapılabilir.
Örnek
Örnek
interface AnimalHunter<T extends Animal> {
void hunt(T animal);
}
class MammutHunter implements AnimalHunter<Mammut> {
void hunt(Mammut m){
}
}
Açıklaması şöyle.
The default relationship in C++ is invariance (i.e. no relationship), e.g. for function arguments, STL containers, and templates in general. An exception are smart pointers and function return types, which are all covariant.Elimizde şöyle bir hiyerarşi olsun.
class Animal
{
public:
virtual std::string type() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal
{
public:
virtual std::string type() const {
return "I am a dog";
}
};
class Cat : public Animal
{
public:
virtual std::string type() const {
return "I am a cat";
}
};
Bu hiyerarşiyi içeren bir container olsun.template <typename T>
class AnimalFarm
{
...
};
Şu kod generics kullanısa bile derlenmez.void farmTest(const AnimalFarm<Animal *> &farm)
{
std::cout << "test farm";
}
AnimalFarm<Dog *> dogFarm;
AnimalFarm<Animal *> animalFarm;
farmTest(animalFarm); // OK
farmTest(dogFarm); // NOK compiler error as class AnimalFarm<Dog *> does not inherit
// from class AnimalFarm<Animal *>
Java'da bu işi halletmek extends ile kolayList<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList; // OK.
C++ iki çözüm var.1. Herşeyi generic yapmak. Şöyle yaparız.
template <typename U>
void farmTest(const AnimalFarm<U *> &farm) {...}
2. AnimalFarm'ın özel bir ata sınıfa sahip olmasını sağlarız. Şöyle yaparıztemplate <typename T, typename = void>
class AnimalFarm // Primary template
{
};
template<typename T>
class AnimalFarm<T*,
std::enable_if_t<
!std::is_same<T, Animal>::value &&
std::is_base_of<Animal, T>::value
>
> // Specialization only instantiated when both conditions hold
// Otherwise SFINAE
: public AnimalFarm<Animal*>
{
};
Hiç yorum yok:
Yorum Gönder