"Size « erişim düzenleyicilerden » bahsedeceğim . Onlardan daha önce bahsetmiştim ama tekrar, öğrenmenin temel direğidir."

Diğer sınıfların, sınıfınızın yöntemlerine ve değişkenlerine erişimini (görünürlüğünü) kontrol edebilirsiniz. Bir erişim düzenleyici, «Bu yönteme/değişkene kim erişebilir?» sorusunu yanıtlar. Her yöntem veya değişken için yalnızca bir değiştirici belirtebilirsiniz.

1) « genel » değiştirici.

Public değiştirici ile işaretlenmiş bir değişkene, metoda veya sınıfa programın herhangi bir yerinden erişilebilir. Bu, en yüksek açıklık derecesidir: herhangi bir kısıtlama yoktur.

2) « özel » değiştirici.

Özel değiştirici ile işaretlenmiş bir değişkene, yönteme veya sınıfa yalnızca tanımlandığı sınıftan erişilebilir. İşaretlenen yöntem veya değişken, diğer tüm sınıflardan gizlenir. Bu, en yüksek gizlilik derecesidir: yalnızca sınıfınız tarafından erişilebilir. Bu tür yöntemler miras alınmaz ve geçersiz kılınamaz. Ek olarak, alt sınıftan bunlara erişilemez.

3)  « Varsayılan değiştirici».

Bir değişken veya yöntem herhangi bir değiştirici ile işaretlenmemişse, "varsayılan" değiştirici ile işaretlenmiş olarak kabul edilir. Bu değiştiriciye sahip değişkenler ve yöntemler, tanımlandıkları paketteki tüm sınıflar tarafından ve yalnızca bu sınıflar tarafından görülebilir. Bu değiştirici aynı zamanda " paket " veya " özel paket " erişimi olarak da adlandırılır ve değişkenlere ve yöntemlere erişimin, sınıfı içeren paketin tamamına açık olduğu gerçeğini ima eder.

4) « korumalı » değiştirici.

Bu erişim düzeyi package ' den biraz daha geniştir . Protected değiştiricisiyle işaretlenmiş bir değişkene, yönteme veya sınıfa paketinden ("paket" gibi) ve tüm miras alınan sınıflardan erişilebilir.

Bu tablo her şeyi açıklıyor:

Görünürlük türü anahtar kelime Erişim
Senin sınıfın Paketin soyundan gelen Tüm sınıflar
Özel özel Evet HAYIR HAYIR HAYIR
paket (değiştirici yok) Evet Evet HAYIR HAYIR
Korumalı korumalı Evet Evet Evet HAYIR
Halk halk Evet Evet Evet Evet

Bu tabloyu kolayca hatırlamanın bir yolu var. Bir vasiyet yazdığınızı hayal edin. Tüm eşyalarını dört kategoriye ayırıyorsun. Eşyalarını kim kullanacak?

Kimin erişimi var? Değiştirici Örnek
sadece  ben özel Kişisel günlük
Aile (değiştirici yok) Aile fotoğrafları
Aile ve mirasçılar korumalı Aile mülkü
Herkes halk Anılar

"Aynı paketteki sınıfların bir ailenin parçası olduğunu hayal etmeye çok benziyor."

"Ayrıca, geçersiz kılma yöntemleriyle ilgili bazı ilginç nüanslardan bahsetmek istiyorum."

1) Soyut bir yöntemin örtük uygulaması.

Diyelim ki aşağıdaki koda sahipsiniz:

kod
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

Ve bu sınıfı devralan bir Tiger sınıfı oluşturmaya ve yeni sınıfa bir arayüz eklemeye karar verdiniz.

kod
class Cat
{
 public String getName()
 {
   return "Oscar";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

IntelliJ IDEA'nın uygulamanızı söylediği tüm eksik yöntemleri uygularsanız, daha sonra bir hatayı aramak için uzun zaman harcayabilirsiniz.

Tiger sınıfının, HasName arabirimi için getName yönteminin uygulanması olarak alınacak olan, Cat'ten miras alınan bir getName yöntemine sahip olduğu ortaya çıktı.

"Bunda korkunç bir şey görmüyorum."

"Çok kötü değil, hataların ortaya çıkması muhtemel bir yer."

Ama daha da kötüsü olabilir:

kod
interface HasWeight
{
 int getValue();
}
interface HasSize
{
 int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
 public int getValue()
 {
  return 115;
 }
}

Birden çok arabirimden her zaman miras alamayacağınız ortaya çıktı. Daha doğrusu, onları miras alabilirsin ama doğru bir şekilde uygulayamazsın. Örneğe bakınız. Her iki arabirim de getValue() yöntemini uygulamanızı gerektirir, ancak bunun ne döndürmesi gerektiği açık değildir: ağırlık mı yoksa boyut mu? Bununla uğraşmak zorunda olmak oldukça tatsız.

"Kabul ediyorum. Bir yöntemi uygulamak istiyorsunuz ama uygulayamıyorsunuz. Zaten temel sınıftan aynı ada sahip bir yöntemi miras aldınız. Bu yöntem bozuk."

"Ama iyi haberler var."

2) Genişleyen görünürlük. Bir türü devraldığınızda, bir yöntemin görünürlüğünü genişletebilirsiniz. İşte böyle görünüyor:

java kodu Tanım
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
protectedYöntemin görünürlüğünü public.
kod Bu neden «yasal»
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
Her şey yolunda. Burada, soyundan gelen bir sınıfta görünürlüğün genişletildiğini bile bilmiyoruz.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
Burada görünürlüğü genişletilmiş olan yöntemi çağırıyoruz.

Bu mümkün olmasaydı, Tiger'da her zaman bir yöntem bildirebilirdik:
public String getPublicName()
{
super.getName(); //korumalı yöntemi çağır
}

Yani herhangi bir güvenlik ihlalinden bahsetmiyoruz.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
Bir temel sınıfta ( Cat ) bir yöntemi çağırmak için gerekli tüm koşullar karşılanırsa, o zaman yöntemi alt türde ( Tiger ) çağırmak için kesinlikle karşılanırlar . Çünkü metot çağrısı üzerindeki kısıtlamalar zayıftı, güçlü değildi.

"Tam olarak anladığımdan emin değilim ama bunun mümkün olduğunu hatırlayacağım."

3) Dönüş türünün daraltılması.

Geçersiz kılınan bir yöntemde, dönüş türünü daraltılmış bir referans türüne değiştirebiliriz.

java kodu Tanım
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
Yöntemi geçersiz kıldık getMyParentve şimdi bir nesne döndürüyor Tiger.
kod Bu neden «yasal»
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Her şey yolunda. Burada getMyParent yönteminin dönüş türünün soyundan gelen sınıfta genişletildiğini bile bilmiyoruz.

«Eski kod» nasıl çalıştı ve çalışıyor.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
Burada dönüş tipi daraltılmış olan metodu çağırıyoruz.

Bu mümkün olmasaydı, Tiger'da her zaman bir yöntem bildirebilirdik:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

Yani herhangi bir güvenlik ihlali ve/veya tip dökümü ihlali söz konusu değildir.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Değişkenlerin türünü temel sınıfa (Cat) genişletmiş olsak da burada her şey yolunda gidiyor.

Geçersiz kılma nedeniyle doğru setMyParent yöntemi çağrılır.

Ve getMyParent yöntemini çağırırken endişelenecek bir şey yok , çünkü geri dönüş değeri, Tiger sınıfından olsa da, temel sınıfın (Cat) myParent değişkenine sorunsuz bir şekilde atanabilir .

Tiger nesneleri, hem Tiger değişkenlerinde hem de Cat değişkenlerinde güvenle saklanabilir.

"Evet. Anlaşıldı. Yöntemleri geçersiz kılarken, nesnelerimizi yalnızca temel sınıfı işleyebilen ve sınıfımız hakkında hiçbir şey bilmeyen koda iletirsek, tüm bunların nasıl çalıştığının farkında olmalısınız. "

"Kesinlikle! O zaman asıl soru, bir yöntemi geçersiz kılarken neden dönüş değerinin türünü daraltamıyoruz?"

"Bu durumda temel sınıftaki kodun çalışmayı durduracağı açık:"

java kodu sorunun açıklaması
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
getMyParent yöntemini aşırı yükledik ve dönüş değerinin türünü daralttık.

Burada her şey iyi.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
Daha sonra bu kod çalışmayı durduracaktır.

GetMyParent yöntemi, aslında bir Tiger nesnesinde çağrıldığından, bir Nesnenin herhangi bir örneğini döndürebilir.

Ve atamadan önce bir çekiniz yok. Bu nedenle, Cat tipi myParent değişkeninin bir String referansı saklaması tamamen mümkündür .

"Harika bir örnek, Amigo!"

Java'da, bir yöntem çağrılmadan önce, nesnenin böyle bir yöntemi olup olmadığı kontrol edilmez. Tüm kontroller çalışma zamanında gerçekleşir. Ve eksik bir yönteme yapılan [varsayımsal] bir çağrı, büyük olasılıkla programın var olmayan bayt kodunu yürütmeye çalışmasına neden olur. Bu sonuçta ölümcül bir hataya yol açar ve işletim sistemi programı zorla kapatır.

"Vay. Artık biliyorum."