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


26 Kasım 2015 Perşembe

Random Number Generator Kullanımı

Math.Random
Bu sınıfın açıklamasında [0 - 1.0) arasında sayı üretir yazıyor. Kastedilen şey [0-0.9999999999999) arasında sayı üretir gibi bir cümle. Evet 1.0 hiç bir zaman üretilmez ama bu sayıya çok yakın bir sayı da üretebilir. Dolayısıyla aşağıdaki kod [0-26] arasında bir sonuç döner
System.out.println((int)(Math.random() * 27));
Random
Random Sınıfı yazısına taşıdım.

Belli Aralıklarda Int Sayı Üretmek
Java 7'den önce
Temel formül şu
minimum + rn.nextInt(maxValue - minvalue + 1)
Formül şöyle çalışıyor. Diyelimki [10 - 20] arasında sayı üretmek istiyoruz.
nextInt metoduna geçilen maxValue - minvalue + 1 ile [0,11) arasında sayı üretiriz. Üzerine 10 ekleyince [10,21) arasında sayı üretiriz.

Şöyle yaparız.
public static int randInt(int min, int max) {
    Random rand (...); //initialize
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}
C#'ta bu işi hazır olarak yapan Next(int minValue,int maxValue) metodu var.

Java 7'den sonra
ThreadLocalRandom yazısına taşıdım

Belli Aralıklarda Double Sayı Üretmek
Eksi- Artı Aralığında NextDouble
Eğer istenilen aralık [x,y] eksi sayıdan artı bir sayıya kadarsa nextDouble() veya nextInt() metoduna geçilecek sayı, aralığın 2 katı * [-0.5 - +0.5] arasında sayı üreten üreteç olmalı. [-PI, + PI] arasında sayı üretme örneği:
private static readonly Random rand = new Random();

void DrawRandom()
{
    float angle = 2 * Math.PI * (rand.NextDouble() - 0.5);
    float x = Math.Sin(angle);
    float y = Math.Cos(angle);
    // do something
}
4 Haneli Sayı 1-6 Rakamlarını Kullanan Sayı Üretmek
Basit bir örnek şöyle. Yukarıdaki aralık formülünün burada da uygulandığı görülebilir.
Random r = new Random();
int number = 0;
int d = 1;

while (number < 1000) {

    // generate number between 1-6
    int num = r.nextInt(6) + 1;

    number += num * d;
    // increase the digit (units, tens, hundreds, thousands)
    d *= 10;
}

System.out.println(number);
Dağılım Sınıfı
Java'da dağılım sınıfı yok. Bir tek Gaussian dağılım yapan, nextGaussian() metodu var.
Random rnd = new Random();
double value = rnd.nextGaussian();