15 Aralık 2015 Salı

Reflection ile Nesne Yaratmak

Giriş
Java'da reflection ile nesne yaratmak için bir şekilde Class sınıfına erişip ya newInstance() metodu çağrılıyor ya da Class sınıfı aracılığıyla Constructor nesnesi bulunup nesne yaratılıyor.

Class Nasıl Bulunur ?
2 yöntem var. 
1. ClassLoader 
2. Class.forName()

1. ClassLoader - Tam İsim ile Class'ı Bulmak
Java'da Class ve ClassLoader ile sınıfları yükleyip yaratabilmek mümkün. ClassLoader'ı kullanmaya hiç ihtiyaç duymadım. Her class kendisini yükleyen ClassLoader'ı bilir.
ClassLoader cl =  t.getClass().getClassLoader();
ClassLoader arayüzünü gerçekleştiren sınıflardan birisi olan URLClassLoader aşağıdaki gibi yaratılabilir.
File file  = new File("vendorcatalogapi.jar");
URL url = file.toURI().toURL();  
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Daha sonra ClassLoader loadClass() metodu ile Class nesnesini verir.
Class theClass = ClassLoader.loadClass("com.mycompany.SomeImpl");
2. Class.forName - Tam İsim ile Class'ı Bulmak

Class.forName() sadece class ve interface'ler için çalışır. bool gibi primitive tipler ile denersek ClassNotFounException alırız.
try {
   Class.forName( "bool" );
} 
catch( ClassNotFoundException e ) {
...
}
Tam sınıf ismi kullanan basit bir örnek
Class theClass = Class.forName("com.example.Foo");
Eğer sınıfı yüklemek istemiyor sadece var olup olmadığını öğrenmek istiyorsak şöyle yaparız.
Class.forName("com.example.Foo", 
              false, 
              ClassLoader.getSystemClassLoader());
İkinci parametrenin false olması sınıfın yüklenmesini engeller. Sınıfın static constructor metodu varsa çalışmadığını görürürüz.
public class Foo {
    static {
        System.out.println("foo loaded and initialized");
    }
}
Sınıf yoksa ClassNotFoundException alacağımız için, sınıfı yüklemeden varlığını tespit edebiliriz.
Class veya Constructor Bulunduktan Sonra

Elimizde iki seçenek var. 
1. Class sınıfının sunduğu newInstance metodunu kullanmak 
2. Constructor sınıfının sunduğu newInstance metodunu kullanmak. Açıklaması şöyle

1. Class.newInstance metodu
Class bir kere bulunduktan sonra şu kurallara uyuyorsa yeni bir nesne yaratmak çok kolay
Class.newInstance() will only succeed if the constructor is has zero arguments and is already accessible.
Yani
1. Sınıfın default constructor'a sahip olması gerekir.
2. Constructor'ın erişilebilir olması gerekir. Örneğin public olması gerekir.

Eğer hata olursa bu metod IllegalAccessException ve InstantiationException atar.

Basit bir örnek
SomeImpl impl = (SomeImpl)theClass.newInstance();
Generic Örnek
Exceptionlardan kurtulup generic bir metod kullanmak istersek aşağıdaki gibi yapabiliriz. Örnekte Parent sınıfından kalıtan sınıflar yaratılıyor. Ayrıca Java 7 ile gelen çoklu exception yakalama da gösteriliyor.
public static <T extends Parent> T load(Class<T> clazz) {
  try {
    T object = clazz.newInstance();
    return object;
  } catch (IllegalAccessException | InstantiationException e) {
    //handle the exception here...
  }
  return null;
}

2. Constructor.newInstance() metodu - Default Constructor Yoksa Kullanılabilir
Parametre Alan Constructor Örneği 1
Parametre alan constructor'a sahip bir nesne yaratmak için örnek
import java.lang.reflect.Constructor;

...

Class<?> loadedClass = classLoader.loadClass("tmp."+className);
try {
  Constructor<?> ctor=loadedClass.getConstructor(int.class);
  ctor.newInstance(42);
  ...
}
Parametre Alan Constructor Örneği 2
Bu sefer Constructor nesnesi getConstructor ile değil, getDeclaredConstructor ile bulunuyor. Elimizde şöyle bir sınıf olsun.
public Person() {
}

public Person(int age) {
  this.age = age;
}

public Person(int age, String name) {
  this.age = age;
  this.name = name;
}
Şöyle yaparız.
Class<Person> pClass = Person.class;
pClass.getDeclaredConstructor().newInstance(); // default constructor
pClass.getDeclaredConstructor(int.class).newInstance(50);
pClass.getDeclaredConstructor(int.class, String.class).newInstance(50, "test");






4 Aralık 2015 Cuma

Varargs

Giriş
Java'da varags üç nokta ile tanımlanır.
public void foo(Object... bar) 
Bu metoda geçilen parametreler Object[]'e çevrilir.
foo(1,2,3);
foo(new Integer(1), new Integer(2), new Integer(3));

public void foo(Object... bar) {
  System.out.println("bar.getClass().isArray():\t" + bar.getClass().isArray());
}
Bu çağrıların çıktısı olarak şunu alırız.
bar.getClass().isArray():   true
-------------------------------------
bar.getClass().isArray():   true