Чланови објекта и чланови класе¶
У овој лекцији:
По чему се разликују статички методи (тј. методи класе) од нестатичких (тј. метода објекта),
Како да обезбедимо удобно учитавање објеката наше класе.
У претходном примеру смо започели прављење класе Razlomak
и за сада смо омогућили да корисник формира разломке на основу вредности бројиоца и имениоца, као и да пореди те разломке.
Логично је да претпоставимо да ће програмер који користи класу Razlomak
желети да својим корисницима омогући да унесу вредност разломка са тастатуре. Он би то могао да уради на разне начине у неколико линија кода. Пошто вероватно не жели да понавља тих неколико линија за сваки разломак који треба да се унесе, могао би и да напише метод који учитава бројилац и именилац (са или без разломачке црте), а враћа учитани разломак. Међутим, ако је већ потребно да се напише метод за унос разломка са тастатуре, онда је боље да тај метод буде део класе. На тај начин би метод био написан само једном (у класи), уместо да га пише сваки програмер који користи класу Razlomak
.
Према томе, било би добро да проширимо дефиницију наше класе методом за унос разломка. Како са тастатуре директно може да се чита једино текст, написаћемо метод који из датог стринга (који садржи запис разломка) ишчитава вредности бројиоца и имениоца и враћа формирани разломак. то ће омогућити крајњем кориснику (који извршава програм) да уноси разломак онако како се он и иначе пише, на пример 2/3
.
Идеја је да се учитавање разломака обавља као и учитавање целих или реалних бројева:
int i = int.Parse(Console.ReadLine());
double x = double.Parse(Console.ReadLine());
Razlomak r = Razlomak.Parse(Console.ReadLine());
Приметимо да за метод Parse
није потребан објекат класе Razlomak
, из кога би се метод позивао. Природније је да овај метод позивамо навођењем имена класе, тачке и имена метода, као што то радимо и за основне типове. За разлику од овога, методе Equals
и CompareTo
смо позивали навођењем имена објекта, тачке и имена метода, на пример:
if (r1.Equals(r2)) ...
Подсетимо се: методи CompareTo
и Equals
класе Razlomak
користе вредности поља a
и b
да би свој разломак упоредили са другим разломком и израчунали тражену повратну вредност. Од метода Parse
не очекујемо да уопште има свој разломак (а још мање да га користи), него да направи и врати нови разломак.
Очигледно је да постоји важна разлика између метода CompareTo
и Equals
са једне, и метода Parse
са друге стране. Могли бисмо да кажемо да методи CompareTo
и Equals
припадају конкретним разломцима, тј. инстанцама класе Razlomak
(имају свој разломак), док метод Parse
представља функционалност саме класе и нема свој разломак.
Метод који представља функционалност саме класе и нема свој објекат називамо статички метод.
Метод који припада инстанци класе (тј. има свој објекат) називамо нестатички метод.
Статички метод означавамо кључном речју static
испред типа повратне вредности у дефиницији метода. Ево како би статички метод Parse
могао да буде написан:
class Razlomak
{
private int a, b;
...
public static Razlomak Parse(string s)
{
if (String.IsNullOrEmpty(s))
{
throw new Exception("Nije dobar zapis razlomka");
}
int k = s.IndexOf('/');
if (k == -1) { return new Razlomak(int.Parse(s)); }
return new Razlomak(
int.Parse(s.Substring(0, k)),
int.Parse(s.Substring(k + 1)));
}
}
Ово, наравно, није први пут да се срећемо са кључном речи static
. Напротив, ову реч смо виђали вероватно у свим досадашњим програмима на језику C# (приметимо да је метод Main
статички, тј. да његова дефиниција почиње са static void Main
). Међутим, до сада смо реч static
прихватали без дубљег разумевања (просто – тако се пише), а сада јасније разумемо разлику између статичких и нестатичких метода и знамо испред којих метода треба писати ову реч, а испред којих не.
Као што је речено, метод проглашавамо за статички када му није потребан „његов” објекат. Мада смо ово сад разјанили, сигурно ће се дешавати да реч static
употребимо недоследно. На овом месту можемо да проверимо шта би се догодило када бисмо нпр. у статичком методу Parse
ипак поменули поље a
. Додајте на почетак метода Parse
наредбу
Console.WriteLine(a);
и покушајте да преведете програм. Требало би да добијете следећу синтаксну грешку:
Error CS0120 An object reference is required for the non-static field, method, or property 'Razlomak.a'
Ова грешка значи да име поља a
у методу Parse
не може да стоји само за себе (овај метод нема свој објекат), него се захтева да се наведе и објекат коме то поље припада.
Можемо да покушамо и обрнуто, да ван класе Razlomak
креирамо објекат a
те класе и да позовемо „његов” метод Parse
.
static void Main(string[] args)
{
Razlomak a = new Razlomak(2,3);
Razlomak b = a.Parse(Console.ReadLine());
...
У овом случају добијамо следећу синтаксну грешку:
Error CS0176 Member 'Razlomak.Parse(string)' cannot be accessed with an instance reference; qualify it with a type name instead
У преводу: метод Parse
није дохватљив из инстанце класе (зато што је метод статички). Уместо инстанцом, позив треба квалификовати именом типа (тј. класе).
Ако већ покушамо нешто бесмислено (као што је коришћење поља свог објекта из статичког метода, или позив статичког метода из инстанце класе), за нас је најбоље да такав покушај не прође ни компајлирање. Разумевање и исправљање оваквих синтаксних грешака може у почетку да изгледа тешко, али се у ствари брзо учи и сигурно је лакше од налажења грешке настале током извршавања програма.
Поменимо и да смо у досадашњем коду класе Razlomak
већ употребили два статичка метода, које смо редом назвали NZD
и Skrati
:
private static int NZD(int a, int b)
{
// nametnut preduslov: a >= 0 i b >= 0
while (b > 0) { int r = a % b; a = b; b = r; }
return a;
}
private static void Skrati(ref int x, ref int y)
{
// preduslov: x i y nisu oba nule
int d = NZD(Math.Abs(x), Math.Abs(y));
x /= d;
y /= d;
}
Метод NZD
израчунава највећи заједнички делилац два дата ненегативна цела броја и користи се на неколико места као помоћна функција. Метод Skrati
између осталог служи да помоћу њега скратимо разломак који се формира у конструктору и тако успоставимо потребне услове. Методи NZD
и Skrati
, као ни метод Parse
, не захтевају присуство неког објекта класе Razlomak
из кога би се ти методи позивали. У ствари, у методима NZD
и Skrati
се чак ни не помињу објекти класе Razlomak
, па је сасвим природно да их означимо као статичке.
Једна од разлика између ових статичких метода је у томе што је је метод Parse
јаван и намењен позивању из кода ван класе, а методи NZD
и Skrati
приватни и намењени употреби само унутар класе. О томе зашто су методи NZD
и Skrati
приватни ће бити још речи у делу о апстракцији.
Након што смо додали метод Parse
, програмеру који користи класу Razlomak
је сада лако да омогући својим корисницима да задају вредности разломака са тастатуре. Следи пример који можете да испробате у свом радном окружењу.