Садржај

Чланови објекта и чланови класе

У овој лекцији:

  • По чему се разликују статички методи (тј. методи класе) од нестатичких (тј. метода објекта),

  • Како да обезбедимо удобно учитавање објеката наше класе.

У претходном примеру смо започели прављење класе 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 је сада лако да омогући својим корисницима да задају вредности разломака са тастатуре. Следи пример који можете да испробате у свом радном окружењу.

(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+