Önce saf parametrik polimorfizm hakkında konuşalım ve daha sonra sınırlı polimorfizme geçelim.
Parametrik polimorfizm ne anlama geliyor? Bu, bir tür veya daha çok tür yapıcısının bir tür tarafından parametreleştirildiği anlamına gelir . Tür bir parametre olarak iletildiğinden, ne olabileceğini önceden bilemezsiniz. Buna dayanarak herhangi bir varsayımda bulunamazsınız. Şimdi, ne olabileceğini bilmiyorsanız, ne işe yarar? Bununla ne yapabilirsiniz?
Örneğin, saklayabilir ve alabilirsiniz. Daha önce bahsettiğiniz durum budur: koleksiyonlar. Bir öğeyi bir listede veya dizide saklamak için, öğe hakkında hiçbir şey bilmem gerekiyor. Liste veya dizi, türden tamamen habersiz olabilir.
Peki ya Maybe
türü? Bunu bilmiyorsanız, Maybe
belki bir değeri olan ve belki de olmayan bir türdür. Nerede kullanırdın? Örneğin, bir öğeyi sözlükten çıkarırken: bir öğenin sözlükte bulunmaması istisnai bir durum değildir, bu nedenle öğe yoksa gerçekten bir istisna atmamalısınız. Bunun yerine, Maybe<T>
tam olarak iki alt tipi olan bir alt türünün örneğini döndürürsünüz : None
ve Some<T>
. bir istisna veya tüm dans atmak yerine int.Parse
gerçekten geri dönmesi gereken bir şeyin başka bir adayıdır .Maybe<int>
int.TryParse(out bla)
Şimdi, Maybe
bunun sadece sıfır veya bir unsuru olabilen bir liste gibi olduğunu iddia edebilirsiniz. Ve bu yüzden biraz bir koleksiyon.
Sonra ne olacak Task<T>
? Gelecekte bir noktada bir değer döndürmeyi vaat eden, ancak şu anda bir değeri olması gerekmeyen bir tür.
Yoksa ne olacak Func<T, …>
? Tipler üzerinde soyutlama yapamazsanız, bir fonksiyondan bir tipten diğerine bir fonksiyon kavramını nasıl temsil edersiniz?
Ya da, daha genel olarak: İki yeniden o soyutlama dikkate ve olan temel yazılım mühendisliği işlemleri, neden olmaz sen türleri üzerinde soyut edebilmek istiyor?
Şimdi sınırlı polimorfizm hakkında konuşalım. Sınırlı polimorfizm temel olarak parametrik polimorfizm ve alt tip polimorfizminin buluştuğu yerdir: bir tip yapıcısının tür parametresinden tamamen habersiz olması yerine, türü belirli bir türün alt türü olarak bağlayabilir (veya sınırlayabilirsiniz).
Koleksiyonlara geri dönelim. Bir hashtable alın. Yukarıda bir listenin elemanları hakkında hiçbir şey bilmesine gerek olmadığını söyledik. Bir hashtable yapar: onları hash edebileceğini bilmelidir. (Not: C # 'da, tüm nesneler eşitlik için karşılaştırılabildiği gibi tüm nesneler yıkanabilir. Yine de bu, tüm diller için geçerli değildir ve bazen C #' da bile tasarım hatası olarak kabul edilir.)
Bu nedenle, hashtable içindeki anahtar türü için type parametrenizi bir örnek olarak sınırlamak istiyorsunuz IHashable
:
class HashTable<K, V> where K : IHashable
{
Maybe<V> Get(K key);
bool Add(K key, V value);
}
Bunun yerine bunun olup olmadığını düşünün:
class HashTable
{
object Get(IHashable key);
bool Add(IHashable key, object value);
}
value
Oradan çıkacak biriyle ne yapardın ? Onunla hiçbir şey yapamazsın, sadece bunun bir nesne olduğunu biliyorsun. Ve eğer onu tekrarlarsanız, elde ettiğiniz tek şey bildiğiniz bir şeydir IHashable
(bu sadece bir mülke sahip olduğu için size çok fazla yardımcı olmaz Hash
) ve bildiğiniz bir şey object
(size daha az yardımcı olur).
Veya örneğinize dayanan bir şey:
class Repository<T> where T : ISerializable
{
T Get(int id);
void Save(T obj);
void Delete(T obj);
}
Öğe, diskte depolanacağı için serileştirilebilir olmalıdır. Peki ya bunun yerine sahipseniz:
class Repository
{
ISerializable Get(int id);
void Save(ISerializable obj);
void Delete(ISerializable obj);
}
Bir koyarsanız jenerik durumda ile, BankAccount
içinde, bir olsun BankAccount
yöntemleri ve özellikleri gibi olan, arka Owner
, AccountNumber
, Balance
, Deposit
, Withdraw
, vb Bir şey çalışabilirsiniz. Şimdi, diğer dava? Bir koymak BankAccount
ama geri bir olsun Serializable
sadece bir özelliği vardır ki,: AsString
. Bununla ne yapacaksın?
Sınırlı polimorfizmle yapabileceğiniz bazı düzgün hileler de vardır:
F-sınırlı niceleme temel olarak tip değişkeninin kısıtlamada tekrar göründüğü yerdir. Bu bazı durumlarda yararlı olabilir. Bir ICloneable
arayüzü nasıl yazıyorsunuz ? Dönüş türünün uygulayıcı sınıfın türü olduğu bir yöntemi nasıl yazarsınız? MyType özelliğine sahip bir dilde , bu kolay:
interface ICloneable
{
public this Clone(); // syntax I invented for a MyType feature
}
Sınırlı polimorfizmi olan bir dilde, bunun gibi bir şey yapabilirsiniz:
interface ICloneable<T> where T : ICloneable<T>
{
public T Clone();
}
class Foo : ICloneable<Foo>
{
public Foo Clone()
{
// …
}
}
Bunun MyType sürümü kadar güvenli olmadığını unutmayın, çünkü birisinin sadece "yanlış" sınıfını tür yapıcısına geçirmesini engelleyen hiçbir şey yoktur:
class EvilBar : ICloneable<SomethingTotallyUnrelatedToBar>
{
public SomethingTotallyUnrelatedToBar Clone()
{
// …
}
}
Özet Türü Üyeler
Sonuç olarak, soyut tip üyeleriniz ve alt tipleriniz varsa, parametrik polimorfizm olmadan tamamen başarabilir ve yine de aynı şeyleri yapabilirsiniz. Scala başladı dair ilk önemli dil olan bu yönde ilerliyor ile tam olarak örneğin Java ve C # tersi olan bunları kaldırmaya çalışırken sonra jenerik ve.
Temel olarak, Scala'da, tıpkı üye olarak alanlara, özelliklere ve yöntemlere sahip olabileceğiniz gibi, türleriniz de olabilir. Ve tıpkı alanlar, özellikler ve yöntemler bir alt sınıfta daha sonra uygulanacak şekilde soyut bırakılabilir gibi, yazım üyeleri de soyut bırakılabilir. List
C # 'da destekleniyorsa, böyle bir şeye benzeyen koleksiyonlara geri dönelim :
class List
{
T; // syntax I invented for an abstract type member
T Get(int index) { /* … */ }
void Add(T obj) { /* … */ }
}
class IntList : List
{
T = int;
}
// this is equivalent to saying `List<int>` with generics
interface IFoo<T> where T : IFoo<T>
ayrıca hatırladım . Açıkçası bu gerçek hayat uygulaması. örnek harika. ama nedense tatmin olmuyorum. uygun olduğunda ve uygun olmadığında fikrimi almak istiyorum. cevaplar bu sürece bazı katkı var, ama hala kendimi tüm bu konuda rahatsız hissediyorum. garip çünkü dil seviyesi problemleri beni çok fazla rahatsız etmiyor.