Садржај
2 Класе и објекти
2.1 Основни појмови о класама и објектима
3 Генеричке класе
4 Наслеђивање и полиморфизам
5 Примери пројеката са решењима
5.1 Различита кретања
5.2 Квиз
5.4 Приказ рада алгоритама сортирања

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

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

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

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

У претходном примеру смо започели прављење класе 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 класе Razlomak поменули поље a. Додајте на почетак метода Parse наредбу

Console.WriteLine(a);

и покушајте да преведете програм. Требало би да добијете следећу синтаксну грешку:

Error   CS0120  An object reference is required for the non-static field, method, or property 'Razlomak.a'

У преводу: потребна је референца на објекат за нестатичко поље, метод, или својство Razlomak.a. Ово је начин на који ће нам компајлер јављати да име поља a у методу Parse не може да стоји само за себе (јер метод Parse нема свој објекат), него се захтева да се наведе и објекат коме то поље припада.

Можемо да покушамо и обрнуто, да ван класе Razlomak креирамо објекат r1 те класе и да позовемо „његов” метод Parse.

static void Main(string[] args)
{
    Razlomak r1 = new Razlomak(2,3);
    Razlomak r2 = r1.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 је сада лако да омогући својим корисницима да задају вредности разломака са тастатуре. Следи пример који можете да испробате у свом радном окружењу.

Статичке класе

Кључну реч static можемо да пишемо и испред имена класе, чиме цела класа постаје статичка. Таква класа не може да се инстанцира, тј. не могу да се креирају објекти те класе. Самим тим, једини чланови који имају смисла за такву класу су статички чланови, најчешће само статички методи и константе (мада она може да има и статичка поља и друге чланове).

Пример статичке класе је добро позната класа Math из библиотеке. У досадашњем учењу програмирања навикли смо да математичке функције позивамо пишући Math са тачком испред имена функције, на пример Math.Abs(x), Math.Min(x, y), Math.Sqrt(x) итд. Овај запис већ препознајемо као позив статичких метода класе Math, а сад знамо и зашто никада нисмо видели (нити ћемо видети) запис облика

Math m = new Math();

Пошто је реч о статичкој класи, објекте ове класе није могуће креирати, нити је то потребно. Класу Math користимо само као колекцију међусобно сродних функција (метода), а у принципу исто важи за сваку статичку класу, па и за оне које бисмо сами написали. Када би нам била потребна једна таква колекција функција, могли бисмо да групишемо те функције у статичку класу, тј. да имплементирамо потребне функције као методе статичке класе. Име статичке класе обично бирамо тако да асоцира на оно што је заједничко свим њеним методима, као што је случај и са класом Math.

Следећи једноставан пример илуструје како можемо да направимо статичку класу.

Програм исписује

Visina obruca u kosarci je 10 stopa, odnosno 3.05 metara.
Jedna milja ima 5280 stopa.
(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+