Kihagyás

7. előadás

Mutabilitás

Final kulcsszó

Nyilvános, módosíthatatlan belső állapot.

Kitérő: Tömbök

Új, megadott elemű tömböt létre lehet hozni:

int[] s = {1, 2, 3};
s = {1,2,3,4}; // fordítási hiba, helyes leírás:
s = new int[]{1,2,3,4};

final referencia

Egy változó végleges értéke (mint a const C-ben)

final Rational p = new Rational(1, 2); // final **referencia**
p.setNumerator(3); // Ez még lehetséges, mert a referenciát nem írja felül
p = new Rational(1, 4); // Fordítási hiba
final int[] data = new int[2];
data[0] = 3;
data[1] = 2;
data = new int[3]; //Fordítási hiba

A String osztály módosíthatatlan (immutable)

String fortytwo = "42";
String twentyfour = fortytwo.reverse();
String twentyfourhundredfortytwo = twentyfour + fortytwo;

A java.lang.StringBuilder és java.lang.StringBuffer módosítható

StringBuilder sb = new StringBuilder("");
for(char c = 'a'; c <= 'z'; c++){
    sb.append(c).append(,);
}
sb.deleteCharAt(sb.length()-1); //Utolsó vessző eltávolítása
String letters = sb.toString();

A char[] is módosítható

String letters = "";
for( char c = 'a'; c <= 'z'; ++c ){
    letters += (c + ","); // Mindig egy-egy új Stringet hoz létre
}
letters = letters.substring(0, letters.length()-1);

Procedurális / moduláris stílus

Osztály szintű metódus - függvény

Immutabilis, új objektumot ad vissza

public class Rational {
    private final int numerator, denominator;

    public Rational( int numerator, int denominator ){ ... }

    public int numerator(){ return numerator; }
    public int denominator(){ return denominator; }

    public static Rational times( Rational left, Rational right )
    {
        return new Rational( left.numerator * right.numerator,
        left.denominator * right.denominator );
    }
}

Rational p = new Rational(1,3), q = new Rational(1,2);
Rational r = Rational.times(p,q);

Osztály szintű metódus - eljárás

Mutabilis, a bal objektumot módosítja

public class Rational
{
    private int numerator, denominator;

    public Rational( int numerator, int denominator ){ ... }

    public int getNumerator(){ return numerator; }
    public void setNumerator( int numerator ){ ... }
    ...
    public static void multiplyLeftWithRight( Rational left, Rational right )
    {
        left.numerator *= right.numerator;
        left.denominator *= right.denominator;
    }
}

Rational p = new Rational(1,3), q = new Rational(1,2);
Rational.multiplyLeftWithRight(p,q);

Paraméterátadás

  • Érték szerinti
  • Érték-eredmény szerinti
  • Cím szerinti
  • Megosztás szerinti

.--.

  • Szövegszerű helyettesítés
  • Eredmény szerinti
  • Név szerinti
  • Igény szerinti

Paraméterátadás - érték szerinti (call-by-value)

primitív típusú paraméterre

public void setNumerator( int numerator )
{
    this.numerator = numerator;
}

Példa:

public void setNumerator( int numerator ){
    this.numerator = numerator;
    numerator = 0; 
}
Rational p = new Rational(1,3);
int two = 2;
p.setNumerator(two);
println(p); //2/3
System.out.println(two); //2, a `numerator = 0` ellenére is

Paraméterátadás - Megosztás szerinti (call-by-sharing)

referencia típusú paraméterre (a referenciát érték szerint adjuk át)

public static void multiplyLeftWithRight( Rational left, Rational right )
{
    left.numerator *= right.numerator;
    left.denominator *= right.denominator;
}

Példa:

public static void multiplyLeftWithRight( Rational left, Rational right )
{
    left.numerator *= right.numerator; // `left` referencia belső értékét módosítja
    left.denominator *= right.denominator;
    left = new Rational(9,7); // felülírja a `left` REFERENCIÁT!
}
Rational p = new Rational(1,3), q = new Rational(1,2);
Rational.multiplyLeftWithRight(p,q);
println(p); //1/6. Nem lesz 9/7, mert ott csak a REFERENCIÁT írtuk felül, nem annak az értékét

Változó számú paraméterek

static int sum( int[] nums )
{
    int s = 0;
    for( int n: nums ){ s += n; }
    return s;
}

sum( new int[]{1,2,3,4,5,6} );

Helyette nyelvi elem:

static int sum( int... nums )
{
    int s = 0;
    for( int n: nums ){ s += n; }
    return s;
}

sum( 1,2,3,4,5,6 );

Ez, valójában átfordul a feljebb található kódrészletre.

Belső állapot kiszivárogtatása

public class Rational {
    private int[] data;
    ...
    public int getNumerator(){ return data[0]; }
    public int getDenominator(){ return data[1]; }
    public void set( int[] data ){
        if( data == null || data.length != 2 || data[1] <= 0 )
        throw new IllegalArgumentException();
        this.data = data;
    }
}
int[] cheat = {3,4};
Rational p = new Rational(1,2);
p.set(cheat); // betölti a 3/4-et
cheat[1] = 0;
// Mivel a `cheat` referenciaként lett belehelyezve p-be, ezért a felülírás érvényes rá:
System.out.println(p.getDenominator()); // Kimenet: 0

Belső állapot kiszivárogtatása (getterrel)

public class Rational {
    private int[] data;
    ...
    public int getNumerator(){ return data[0]; }
    public int getDenominator(){ return data[1]; }
    public void get(){
        return data; // Az `int[]` referenciát adja vissza
    }
}
Rational p = new Rational(1,2);
int[] cheat = p.get();
cheat[1] = 0;

System.out.println(p.getDenominator()); // Kimenet: 0

Belső állapot kiszivárogtatása (megoldás)

Defenzív másolás

public class Rational
{
    private final int[] data;
    public Rational( int[] data )
    {
        if( data == null || data.length != 2 || data[1] <= 0 )
            throw new IllegalArgumentException();
        this.data = new int[]{ data[0], data[1] };
    }
    public void set( int[] data )
    {
        this.data = new int[]{ data[0], data[1] }
    }
    public int[] get()
    {
        return new int[]{ data[0], data[1] };
    }
}

Módosíthatatlan objektumokat nem kell másolni

Például: név (Stringként), kor (intként)

public class Person
{
    private String name;
    public Person(String name){
        /* `name` hibaellenőrzése  */
        this.name = name;
    }
    public String getName(){return name;}
    public void setName(String name){/* Szintén hibaellenőrzés */this.name = name;}
}

Aliasing tömbön belül

Rational rats[2]; // fordítási hiba
Rational rats[] = new Rational[2]; // = {null,null};
Rational[] rats = new Rational[2]; // gyakoribb
rats[0] = new Rational(1,2);
rats[1] = rats[0]; // A tömb két eleme ugyan arra a referenciára mutat
rats[1].setDenominator(3);
System.out.println(rats[0].getDenominator()); // Kimenet: 3
/**
 * PRE: rats != null and (j != j => rats[i] != rats[j])
 */
public static void increaseAllByOne( Rational[] rats ){
    for(Rational p: rats){
        p.setNumerator(p.getNumerator()+p.getDenominator()s);
    }
}

Aliasing tömbök tömbjén

Rational half = new Rational(1,2);
Rational[] halves = {half, half};
Rational[][] matrix = {halves, halves, halves};

Lambdák

int[] nats = {0,1,2,3,4,5,6};

int[] nats = new int[1000];
for(int i = 0; i < nats.length; ++i)
{
    nats[i] = i;
}

int[] nats = new int[1000];
java.util.Arrays.setAll(nats, i->i);
java.util.Arrays.setAll(nats, i->(int)(100*Math.random()));
public static void main(String[] args){
    java.util.Arrays.sort(args);
    java.util.Arrays.sort(args, (s, z) -> s.length()-z.length()); // Meg lehet adni, hogy mi alapján legyen rendezve
}
java.util.Arrays.stream(nums)
                .filter(i -> i % 2 == 0)
                .map(i -> i/2)
                .limit(10)
                .forEach(i -> System.out.println(i));
java.util.Arrays.stream(nums)
                .forEach(System.out::println);

Jövőhéten nincs személyes jelenléttel előadás!