15.Reflection Mekanizması

Bilindiği gibi Java ismine bytecode denilen bir ara kod sistemi ile çalışmaktadır. Yani derleyici bir arakod üretir. Derleyicinin ürettiği bu arakod JavaVirtualMachine (JRE) tarafından yorumlanarak çalıştırılır. Böyle bir mekanizmanın yapılabilmesi için herhangi bir türe ilişkin bilgilerin bytecode'a yazılması gerekmektedir. Ancak bazı durumlarda bu arakod içerisinden programlama sırasında türlere ilişkn bilgilerinde alınması gerekebilir, işte runtime sırasında(çalışma zamanı) arakoddaki türlere ilişkin bilgilerin elde edilip kullanılmasına reflection denilmektedir.

Java'da reflection mekanizmasının temel sınıfı Class isimli sınıftır. Aslında çalışma zamanı sırasında her türe ilişkin bir Class sınıfı türünden bir nesne yaratılır. Bir sınıfın Class nesnesini almak için temel yollar şunlardır.

  1. <Sınıf ismi>.class elemanı ile o sınıfın Class sınıf türü elde edilebilir.
  2. Class sınıfının static forName() metodu ile

Buna göre bir sınıfın Class sınıf referansı elde edildiğinde o sınıfın metodların, veri elemanları gibi bilgiler elde edilebilir. Hatta çalışma zamanı sırasında ve izin olması durumunda private elemanlarına erişilebilir.

Class sınıfı generic bir sınıftır.

forName() metodu ismi String olarak alır ve çalışma zamanı sırasında yükler. Eğer ilgili sınıfı bulamazsa classNotFoundException nesnesi fırlatır.

/*----------------------------------------------------------------------------------------------------------------------
    Class sınıfının forName metodu
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

public class App {    
    public static void main(String[] args) 
    {        
        try {
            Class<?> sampleClass = Class.forName("org.csystem.Sample");

            System.out.println(sampleClass);            
        }
        catch (ClassNotFoundException ex) {
            System.err.println(ex.getMessage());
        }

    }    
}

class Sample {
    public int a;

    public void foo()
    {
        //...
    }
}

JDBC sınıfları forName() metodunu kullanarak sınıfları yükler. Class elemanı kullanılarak elde edilen Class nesnesi bulunamam ihtimali olmadığından herhangi bir kontrol yapılmasına gerek yoktur.

/*----------------------------------------------------------------------------------------------------------------------
    Sınıf isimlerinin .class property elemanı
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

public class App {    
    public static void main(String[] args) 
    {    
        Class<Sample> sampleClass = Sample.class;

        System.out.println(sampleClass);    
    }    
}

class Sample {
    public int a;

    public void foo()
    {
        //...
    }
}

Doğal türler için ".class" elemanı o türü sarmalayan sınıfa ilişkin Class nesnesini verir.

Reflection mekanizmasına ilişkin sınıfların bir çoğu java.lang.Reflect paketinin altında bulunmaktadır.

Sınıfların Metodlarının ve Veri Elemanlarının Elde Edilmesi

Sınıflarının veri elemanları ve metodların elde edilmesi yine class sınıfı kullanılarak yapılabilir.

Class sınıfının getConstructors isimli metodu, constructor türünden bir dizi döndürmektedir. Burada türe ilişkin tüm constructor'ları bir dizi halinde vermektedir.

/*----------------------------------------------------------------------------------------------------------------------
    Constructor<T> sınıfı be Class sınıfının getConstructors metodu
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.lang.reflect.Constructor;

public class App {    
    public static void main(String[] args) 
    {    
        Class<?> c = Sample.class;

        Constructor<?> [] contructors = c.getConstructors();    

        for (Constructor<?> con : contructors)
            System.out.println(con);
    }    
}

class Sample {
    public Sample()
    {}
    public Sample(int val)
    {}
    public int a;

    public void foo()
    {
        //...
    }
}

Hiçbir cons. yazılmazsa derleyici tarafından verilen default cons bu metod tarafından verilmez.

Bu sınıf özellikle public cons. tutmakta kullanılır.

Constructor sınıfının yararlı bir takım metodları bulunmaktadır. Bu sınıfın örneğin newInstance() metodu ile nesne yaratılması sağlanabilir. Constructor sınıfının newOnstance metodu ile nesne yaratılabilir fakat uygun parametre geçilmelidir.

Constructor sınıfının getParameter metodu ile ilgili başlangıç metodunun parametre sayısı bir dizi olarak elde edilebilir.

Parameter sınıfının getType metodu ile her bir parametrenin türü elde edilebilir. Aşağıdaki kodda örneklendirilmiştir.

Class sınıfının getMethods() isimli metodu o class nesnesine ilişkin metodları elde etmek için kullanılır. Bu metod yine method türünden dizi döndürmektedir. getMethods() metodu taban sınıfa ilişkin tüm metodları da liste olarak vermektedir. Class sınıfının getMethod isimli metodu ilgili isme ilişkin metodu vermektedir. Bu metod başarısırız olursa noSuchMethodException sınıfı türünden Exception fırlatmaktadır.

Method sınıfının invoke metodu elde edilen metodun çağrılması için kullanılmaktadır.

/*----------------------------------------------------------------------------------------------------------------------
    Method sınıfı ve invoke metodu
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class App {    
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
    {    
        Class<?> c = Sample.class;

        try {
            Method m = c.getMethod("foo", int.class);

            m.invoke(new Sample(), 10);
        }
        catch (NoSuchMethodException ex) {
            System.err.println(ex.getMessage());
        }



    }    
}

class Sample {
    public Sample()
    {
        System.out.println("Sample.Sample()");
    }
    public Sample(int val)
    {
        a = val;
        System.out.println("Sample.Sample(int)");
    }
    public int a;

    public void foo()
    {
        System.out.println("foo");        
    }

    public void foo(int val)
    {
        System.out.println(val);
        System.out.println("foo(int)");        
    }
}

Programın çalışma zamanında yaratılmış nesnesi getClass() metodu ile elde edilebilir. getClass metodu referansın dinamik türüne ilişkin sınıfın nesnesini vermektedir.

Sınıf Çalışması


/*----------------------------------------------------------------------------------------------------------------------
    Sınıf Çalışması: Bir sınıf yazınız o sınıf içerisinde int türden parametreli
    foo isimli bir metot varsa onu daha önceden yaratılmış bir nesne için çağırınız    
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class App {    
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
    {    
        Sample s = new Sample();

        Class<?> c = s.getClass();

        try {
            Method m = c.getMethod("foo", int.class);

            m.invoke(s, 20);
        }
        catch (NoSuchMethodException ex) {
            System.out.println("Sınıfta istenilen metot bulunmuyor");
        }        
    }    
}

class Sample {
//    public void foo(int val)
//    {
//        System.out.printf("foo:%d%n", val);
//    }
    //...

}

Class sınıfının getFields() metodu o sınıfa ilişkin static olmayan veri elemanlarına ilişkin Field türünden bir dizi verir.

Field sınıfının getName() metodu ile veri elemanının ismi elde edilebilir.

Anahtar Notlar: Class sınıfının elemanlara ilişkin bilgileri elde eden getXXX metodları getDecleareXXX metotları ile ayrıca bildirilmiştir. Bu metodlar tüm elemanları, yani erişim belirleyicisi yada başka koşul olmaksızın tüm elemanları elde etmek için kullanılır. Field sınıfının getType() metodu ile ilgili veri elemanın türüne ilişkin Class nesnesi elde edilebilir.

Field sınıfının get() metodu ile ilgili veri elemanın değeri elde edilebilir. set metodu ile değeri değiştirilebilir. Class sınıfının getField isimli metodu veri elemanı ismini alarak o veri elemanın field türünü döndürmektedir. getField metodu ilgili veri elemanı bulunamazsa noSuchFieldException türünden bir nesne firlatir.

Sınıfırın Erişim Hakkı Olmayan Elemanlarının Çağrılması

Bir sınıfın herhangi bir elemanına erişimin olup olmadığı Field sınıfının isAccessible metodu ile anlaşılabilir. Field sınıfının setAccesible metodu ile belirli bir veri elemanı erişilebilir hale getirilebilir. Fakat işlem bittikten sonra aksi bir durum olmadıkça eski haline döndürülmelidir.

/*----------------------------------------------------------------------------------------------------------------------
    Sınıfın private elemanlarına erişim
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class App {    
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
    {    
        Class<?> c = Sample.class;

        Constructor<?> cons = c.getDeclaredConstructor();

        cons.setAccessible(true);

        Sample s = (Sample)cons.newInstance();
        System.out.println("Test");
        Sample s1 = (Sample)cons.newInstance();        
    }    
}

class Sample {
    private static Sample ms_instance;

    private Sample()
    {        
        if (ms_instance != null) {                    
            throw new SecurityException("Can not instantiate object");
        }
        else 
            ms_instance = new Sample();

    }

    public static Sample getInstance()
    {
        if (ms_instance == null)
            ms_instance = new Sample();

        return ms_instance;
    }


}

Program içerisinde geçen bir zamanın ölçülmesi için 3. parti bir takım programlar kullanılabilmektedir. Bunu kullanmak için en kolay yöntemlerden bir tanesi System.currrentMilisecond isimli metodu çağırmaktır. Bu metod programın çalışıyor olduğu sistem açıldığı zamandan çağrıldığı zamana kadarki milisaniye sayısını döndürmektedir. Aşağıdaki uygulamada bir döngünün ne kadar sürdüğü bilgisi elde edilmektedir.

/*----------------------------------------------------------------------------------------------------------------------
    System.currenMillis metodu ile basit bir zaman ölçümü
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

public class App {    
    public static void main(String[] args)  
    {
        long start = System.currentTimeMillis();

        int a = 10;

        for (long i = 0; i < 10000000L; ++i)
            System.out.println(i);

        long end = System.currentTimeMillis();

        System.out.printf("Time:%.2f%n", (end - start) / 1000D);

    }    
}

results matching ""

    No results matching ""