Witaj w przedostatnim wpisie z tego cyklu. Poza wielkim podsumowaniem czego w Javie nie ma, a jest w C# wypadałoby jeszcze coś powiedzieć o przeciążaniu metod w C# i w Javie, ponieważ różnice są.
Zacznijmy od C#.
Elementy klasy pochodnej mogą redefiniować metodę klasy bazowej. W klasie pochodnej tworzymy metodę o tej samej nazwie i liście parametrów, co metoda w klasie pochodnej.
Metoda ta będzie miała nowe zachowanie. Inne niż metoda z klasy bazowej.
class Rectangle
{
public int x = 1, y = 10;
public int GetArea() { return x * y; }
}
class Square : Rectangle
{
public int GetArea() { return 2 * x; }
}
Istnieje mała, ale poważna różnica pomiędzy ich ukrywaniem , a ich przeciążaniem. Obecnie nadpisaliśmy metodę w C#.
Kompilator nawet podpowie, że zapewne chodziło nam o ukrycie metody.
By to zaznaczyć w C# korzystam ze słowa kluczowego new.
class Rectangle
{
public int x = 1, y = 10;
public int GetArea() { return x * y; }
}
class Square : Rectangle
{
public new int GetArea() { return 2 * x; }
}
Statyczne metod zachowują się podobnie.
class Rectangle
{
public int x = 1, y = 10;
public static int GetArea(int x,int y)
{ return x * y; }
}
class Square : Rectangle
{
public static new int GetArea(int x, int y)
{ return x * y; }
}
A co jeśli chcemy przeciążać metodę. Najpierw metoda w klasie bazowej musi być oznaczona jako wirtualna przy pomocy słowa kluczowego virtual
Statyczne metody nie mogą być wirtualne.
class Rectangle
{
public int x = 1, y = 10;
public virtual int GetArea() { return x * y; }
}
W klasie pochodnej przeciążenie jest określone słowem override.
class Square : Rectangle
{
public override int GetArea() { return 2 * x; }
}
Jaka jest w końcu ta różnica pomiędzy przeciążeniem a nadpisaniem metody poza słowami kluczowymi?
Różnice widać, gdy definicja kwadratu jest umieszczona w klasie bazowej określającej prostokąty. Jeżeli metoda jest nadpisana ze słowem kluczowym new, wtedy wykona się metoda z Prostokątów. Nowa definicja metody z kwadratu będzie ukryta w takim przypadku.
class Program
{
public static void Main(string[] args)
{
Rectangle s1 = new Square();
Square s2 = new Square();
Console.WriteLine(s1.GetArea());
Console.WriteLine(s2.GetArea());
}
}
Jeżeli jednak przeciążyliśmy metodę wirtualną, wtedy w zmiennej Prostokąta przechowującej kwadrat wykona się metoda z kwadratu.
Bazowo kod zawsze wykona się tak samo bez względu na to, jaka referencja obiektu jest przetrzymywana.
W Javie wszystkie metody w klasie są wirtualne
W Javie wszystkie metody są wirtualne oznacza to, że nie trzeba ich definiować jako takich.
Domyślnie metody niestatyczne w klasie są więc przeciążane.
class Rectangle
{
public int w = 10, h = 10;
public int getArea() { return w * h; }
}
class Triangle extends Rectangle
{
public int getArea() { return w * h / 2; }
}
Tworzy to jednak pewien problem. Od Javy 5 została dodana specjalna adnotacja, która upewnia kompilator, że jest to zachowanie, które chcemy mieć.
Tak chcę przeciążyć tę metodę.
class Triangle extends Rectangle
{
@Override public int getArea()
{
return w * h / 2;
}
}
Bez względu na to, czy dodasz adnotację kod przeciąży metody i zachowa się tak samo. Wykona się kod trójkąta.
public static void main(String[] args)
{
Rectangle r = new Triangle();
Triangle t = new Triangle();
System.out.print(r.newArea());
System.out.print(t.newArea());
}
Te twierdzenia w Javie są prawidłowe dla metod w instancji klasy, a co z metodami statycznymi.
A co z metodami statycznymi w Javie
Ukrywanie metod w Javie wystąpi w przypadku metod statycznych.
W końcu metody statyczne nie mogą być wirtualne.
W Javie jednak nie musimy używać słowa kluczowego new by określać, że tak o takie zachowanie nam chodziło.
class Rectangle
{
public int w = 10, h = 10;
public static int newArea(int a, int b) {
return a * b;
}
}
class Triangle extends Rectangle
{
public static int newArea(int a, int b) {
return a * b / 2;
}
}
Adnotacja Override nie może zostać użyta, ponieważ metoda statyczna nie może być przeciążana i adnotacja tego nie zmieni.
Podsumowując w Javie metody instancji klasy zawszę będą przeciążane. Nie można zmienić tego zachowania jak w C#.
Metody statyczne klasy zawszę będą ukrywane i w C# i w Javie.
Jak przeciw działać nadpisywaniu i ukrywaniu
W Javie, by zapobiec nadpisywaniu metody korzystam ze słowa kluczowego final w klasie bazowej.
public final int getArea() { return w * h; }
W C# mamy do tego celu słowo kluczowe sealed.
class MyClass
{
public sealed override int NonOverridable() {}
}
Uzyskanie dostępu do bazowych definicji metod
Aby uzyskać do działania metody z klasy bazowej w Javie używamy słowa kluczowego super.
@Override public int getArea()
{
return super.getArea() / 2;
}
W C# w tym celu używamy słowa kluczowego base.
class Triangle : Rectangle
{
public override GetArea() { return base.GetArea()/2; }
}
Jeśli metoda bazowa przyjmuje parametry to podajemy je w nawiasach.
public Triangle(int a, int b) { super(a,b); }
W podobny sposób możemy skorzystać z bazowego konstruktora wewnątrz konstruktora klasy pochodnej.
public Triangle() { super(); }
Wywołanie konstruktora bazowego musi być zawsze wywołane jako pierwsze.
W C# zachowanie jest podobne. Jedyna różnica polega na słowie kluczowym base oraz na fakcie, że wywołanie kodu bazowego konstruktora odbywa się w sygnaturze konstruktora .
class Rectangle
{
public int x = 1, y = 10;
public Rectangle(int a, int b) { x = a; y = b; }
}
class Square : Rectangle
{
public Square(int a) : base(a,a) {}
}
Kompilator C# i Javy automatycznie dodaje wywołanie bazowego konstruktora, jeśli jest on bezparametrowy.
class Square : Rectangle
{
public Square(int a) {} // : base() implicitly added
}
Zapewnia nas to, że elementy klasy bazowej zostaną utworzone poprawnie dla klasy pochodnej.