Mam mały problemik z autoboxingiem. Potrzebuję delegować argumenty jednego konstruktora do konstruktora zupełnie innej klasy. Nie byłoby z tym większego problemu gdyby nie to, że java obecnie opakowuje wszelkie typy proste wersjami klasowymi (np. Integer zamiast int) w momencie gdy używa się rozwiązania ze zmienną liczbą argumentów wywołania metody. Efekt ten utrudnia znalezienie konstruktora, którego argumenty pasują do tych, które otrzymuję (bo typy argumentów są inne). Co prawda poradziłem sobie z tym problemem w sposób siłowy i mało elegancki, ale podejrzewam, że nie udało mi się wyhaczyć wszystkich sztuczek z klasą Class<?>, chociaż usilnie szukałem. Być może jest nawet metoda, która czasowo likwiduje autoboxing dla zmiennej liczby argumentów lub metoda, która pozwala "odwrócić" skutki autoboxingu, albo uzyskać tablicę Class<?>[] typów parametrów dla argumentów: Object... args. Tego ostatniego nigdzie nie mogę znaleźć.

Żeby było jasne o co mi chodzi, to wrzucę tu całą niepociętą klasę (bo i tak może się przydać osobom zaczynającym bawić się w Swingu). Chciałbym wyeliminować metodę dopasowanyKonstruktor() i wszystkie pozostałe, które ona uruchamia.

package Olamagato;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.lang.reflect.Constructor;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
 * Klasa obsługująca otwieranie, zamykanie i aktualizowanie wycentrowanego
 * okna aplikacji. Dziedziczenie po tej klasie pozwala zmodyfikować
 * proces otwierania okien przez modyfikację metod chronionych.
 * Pozwala na uruchamianie w dowolnym wątku.
 * Sposób użycia:
 * 1. Najprostszy (okno wycentrowane, zmaksymalizowane, deleguje do new MyJFrame())
 * new Okno(MyJFrame.class, true).otwórz();
 * 2.Ze sprawdzeniem poprawności (deleguje do new SmartFrame(String, double))
 * Okno o = new Okno(SmartFrame.class, "Blackhole Recognizer", 2.3);
 * if(o.jestOK())
 * {
 *	if(o.otwórz) System.Out.println("otwarto okno\n");
 *	else { System.Out.println("Błąd wewnętrzny\n"); return; }
 *	...
 *	o.aktualizujLAF()
 *	...
 *	o.zamknij();
 * }
 * else
 *	System.Out.println("Błąd: " + o.dajBłąd().printStackTrace());
 * 3. Z podmianą sposobu uruchomienia i sprawdzeniem (deleguje do new Replacer(String, double, String))
 * 	Okno okno = new Okno(Replacer.class, "Replacer", 1.1, "Windows Classics" )
 *	{
 *		@Override protected void ustawSposóbZamknięcia()
 *			{ okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }
 *
 *		@Override protected void inicjujPoPokazaniu()
 *			{ ((Replacer)okno).setCurDir(); }
 *	};
 *	if(okno.jestOK())
 *		okno.otwórz();
 *	else
 *		System.Error.println("Wiszę Ci piwo.");
 * @author Olamagato
 * @version 1.0
 * @license GNU GPL
 */
public class Okno
{
	/**
	 * Tworzy obiekt okna reprezentowany przez stałą cls w wątku Swinga
	 * używając w tym celu bezparametrowego konstruktora domyślnego. W wypadku
	 * jakiegokolwiek niepowodzenia pole okno pozostanie z wartością null,
	 * a żadna metoda publiczna nie powiedzie się w takim wypadku.
	 * Jeżeli obiekt Okno zostanie uruchomiony już w wątku Swinga, to
	 * okno zostanie uruchomione w bieżącym wątku. Po konstrukcji obiektu
	 * okna stała staje się niepotrzebna bo i tak jest do uzyskania z obiektu.
	 * @param cls obiekt informacji o klasie
	 */
	public Okno(Class<? extends JFrame> cls, Object... args)
	{
		try
		{
			ustawLAF();
			if(SwingUtilities.isEventDispatchThread())
				utwórzOkno(cls, args).run();
			else
				//konstrukcja obiektu JFrame w wątku Swinga musi być zakończona
				//przed zakończeniem tego konstruktora bo to jedyna gwarancja
				//uniknięcia rozsynchronizowania pól okno i błądWykonania
				SwingUtilities.invokeAndWait(utwórzOkno(cls, args));
		}
		catch(Exception ex) //łapiemy wszystkie wyjątki
			{ if(błądWykonania == null) błądWykonania = ex; }
	}

	/**
	 * Umożliwia wymuszenie maksymalizacji okna na domyślnym pulpicie
	 * @param zmaksymalizowane wymusza na systemie powiększenie okna
	 * @see Okno(Class<? extends JFrame> cls, Object... args)
	 */
	public Okno(Class<JFrame> cls, boolean zmaksymalizowane, Object... args)
	{
		this(cls, args);
		this.zmaksymalizowane = zmaksymalizowane;
	}

	/**
	 * @return true jeżeli obiekt okna został utworzony i zainicjowany
	 */
	final public boolean jestOK() { return błądWykonania == null && okno != null; }

	/**
	 * @return przekazuje obiekt najwcześniejszego wyjątku, który spowodował
	 * awarię utworzenia okna
	 */
	final public Exception dajBłąd() { return błądWykonania; }

	/**
	 * Otwiera okno, którego klasa podana była w konstruktorze
	 * @return true jeżeli otwarcie okna zostało wykonane
	 */
	final public boolean otwórz()
	{
		if(okno == null) return false;
		uruchom(new Runnable()
		{
			/**
			 * Dla okno nadaje nazwę, ustawia jako główne okno, ustala
			 * wymiary i położenie, ewentualnie powiększa do maksimum,
			 * inicjuje wstępnie, pokazuje na ekranie i inicjuje stan
			 * początkowy komponentów ekranowych
			 */
			@Override public void run()
			{
				if(okno == null)
					return;
				ustawSposóbZamknięcia();
				ustalWymiary();
				pozycjonujOkno();
				if(zmaksymalizowane)
					okno.setExtendedState(JFrame.MAXIMIZED_BOTH);
				inicjujPrzedPokazaniem();
				okno.setVisible(true);
				inicjujPoPokazaniu();
			}
		});
		return true;
	}

	/**
	 * Zamyka okno, którego klasa podana była w konstruktorze
	 * @return true jeżeli zamknięcie okna zostało wykonane
	 */
	final public boolean zamknij()
	{
		if(okno == null) return false;
		uruchom(new Runnable()
			{ @Override public void run() { okno.dispose(); } });
		return true;
	}

	/**
	 * Aktualizuje wygląd całości komponentu ekranowego
	 * W trakcie aktualizacji zmienia kursor na klepsydrę, a po
	 * jej wykonaniu przywraca poprzedni stan kursora.
	 */
	final public void aktualizujLAF()
	{
		if(okno == null) return ;
		uruchom(new Runnable()
		{
			@Override public void run()
			{
				Cursor obecny = okno.getCursor();
				okno.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
				SwingUtilities.updateComponentTreeUI(okno);
				okno.validate();
				okno.setCursor(obecny);
			}
		});
	}

	/**
	 * Uruchamia program typu Runnable zawsze w wątku Swing. Jeżeli obecny
	 * wątek nie jest Swing, to uruchomienie jest asynchroniczne.
	 * @param programSwing do uruchomienia
	 */
	private void uruchom(Runnable programSwing)
	{
		if(SwingUtilities.isEventDispatchThread())
			programSwing.run();
		else
			SwingUtilities.invokeLater(programSwing);
	}

	/**
	 * Ustawia wygląd okna. Domyślnie próbuje ustawić wygląd systemowy
	 */
	protected void ustawLAF()
	{
		try
		{
			UIManager.setLookAndFeel(
				UIManager.getSystemLookAndFeelClassName());
		}
		catch(UnsupportedLookAndFeelException e){}
		catch(ClassNotFoundException e){}
		catch(InstantiationException e){}
		catch(IllegalAccessException e){}
	}

	/**
	 * Definiuje reakcję na zamknięcie okna. Domyślnie usuwa okno.
	 */
	protected void ustawSposóbZamknięcia()
	{
		okno.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}

	/**
	 * Definiuje sposób ustalenia wymiarów okna. Domyślnie uzależnia
	 * rozmiar od zawieranych przez niego komponentów.
	 */
	protected void ustalWymiary()
		{ okno.pack(); }

	/**
	 * Ustawia początkową pozycję okna. Domyślnie centruje okno z
	 * uwzględnieniem szerokości pasków narzędziowych na pulpicie
	 * ograniczających pole ekranu z każdej strony. W razie ich
	 * istnienia i wielkości okna mniejszego niż dostępne pole ekranu
	 * przesuwa okno tak aby znalazło się całkowicie w polu ekranu.
	 * Jeżeli rozmiar jest zbyt mały w jednym lub obu wymiarach, to
	 * przesuwa okno tak aby lewy górny róg okna znajdował się w tym polu.
	 */
	protected void pozycjonujOkno()
		{ okno.setLocation(punktCentrowania(okno)); }

	/**
	 * Definiuje akcje przed samą operacją utworzenia okna na ekranie.
	 * Domyślnie nic nie wykonuje.
	 */
	protected void inicjujPrzedPokazaniem() {}

	/**
	 * Definiuje akcje tuż po operacji utworzenia okna na ekranie
	 * Domyślnie nic nie wykonuje.
	 */
	protected void inicjujPoPokazaniu() {}

	///kod centrujący okno////////////////////////////////////////////////////

	/**
	 * Oblicza punkt zaczepienia lewego-górnego roku okna centrując
	 * o ile to możliwe okno na ekranie i przesuwając je tak aby
	 * w całości znalazło się w dostępnym polu ekranu.
	 * Marginesy ekranu są szerokością toolbarów na każdej z możliwych
	 * krawędzi pulpitu. W Windows domyślnym dolnym toolbarem jest Explorer
	 * Olewają je: Java, MS Office itd.
	 * @param komponent obiekt centrowanego okna
	 * @return punkt zaczepienia lewego-górnego rogu okna
	 */
	static Point punktCentrowania(Component komponent)
	{
		//prostokąt ekranu w układzie współrzędnych
		Rectangle ekr = komponent.getGraphicsConfiguration().getBounds();
		//wielkości marginesów ekranu
		Insets marginesyEkranu = Toolkit.getDefaultToolkit().getScreenInsets(
			komponent.getGraphicsConfiguration());
		Dimension okno = komponent.getSize();//wymiary okna
		//nowe miejsce okienka w lewym górnym rogu dostępnego ekranu
		return punktCentrowania(ekr, marginesyEkranu, okno);
	}

	static Point punktCentrowania(final Rectangle ekran,
		final Insets marginesyEkranu, Dimension okno)
	{
		Rectangle ok =
			new Rectangle(ekran.x, ekran.y, okno.width, okno.height);
		dopasujOkno(ok, ekran, marginesyEkranu, true);
		return new Point(ok.x, ok.y);
	}

	static Rectangle dopasujOkno(Rectangle okno, final Rectangle ekran,
		final Insets mrgekr, boolean centruj)
	{
		final boolean w_poziomie =
			okno.width < ekran.width - (mrgekr.left + mrgekr.right);
		final boolean w_pionie =
			okno.height < ekran.height - (mrgekr.top + mrgekr.bottom);
		if(centruj)
			centruj(okno, ekran, w_poziomie, w_pionie);
		//Uwzględnianie marginesów ekranu (mrgekr)
		//ewentualnie przesuwamy początek okna przed l-g marginesy ekranu
		if(okno.x < ekran.x + mrgekr.left) okno.x += mrgekr.left;
		if(okno.y < ekran.y + mrgekr.top) okno.y += mrgekr.top;
		Point koniec_ekranu = new Point(//Prawy dolny róg ekranu
			ekran.x + ekran.width - mrgekr.right,
			ekran.y + ekran.height - mrgekr.bottom);
		Point koniec_okna = new Point(//Prawy dolny róg okienka
			okno.x + okno.width, okno.y + okno.height);
		//ewentualnie przesuwamy koniec okna przed p-d marginesy ekranu
		if(w_poziomie && koniec_ekranu.x < koniec_okna.x)
			okno.x -= koniec_okna.x - koniec_ekranu.x;
		if(w_pionie && koniec_ekranu.y < koniec_okna.y)
			okno.y -= koniec_okna.y - koniec_ekranu.y;
		return okno;
	}

	static void centruj(Rectangle okno, final Rectangle ekran,
		boolean w_poziomie, boolean w_pionie)
	{	//nowy punkt zaczepienia okna jeżeli centrowanie nie jest możliwe
		okno.x = ekran.x;
		okno.y = ekran.y;
		//centrujemy pod warunkiem, że rozmiar okna mieści się w
		//ekranie ograniczonym przez marginesy z obu stron
		if(w_poziomie)
			okno.x += (ekran.width >> 1) - (okno.width >> 1);
		if(w_pionie)
			okno.y += (ekran.height >> 1) - (okno.height >> 1);
	}

	///konstrukcja delegowanego okna//////////////////////////////////////////

	/**
	 * Zwraca obiekt Runnable pozwalający na utworzenie obiektu okna
	 * używając do tego celu konstruktora pasującego do listy argumentów
	 * @param cls typ
	 * @param args argumenty wywołania żądanego konstruktora
	 * @return Obiekt Runnable pozwalający wywołać konstruktor
	 * @throws SecurityException brak dostępności konstruktora publicznego
	 * @throws NoSuchMethodException brak żądanego konstruktora
	 * bez parametrów.
	 */
	private Runnable utwórzOkno(Class<? extends JFrame> cls,
		final Object[] args) throws SecurityException, NoSuchMethodException
	{
		final Constructor<? extends JFrame> konstruktor =
			dopasowanyKonstruktor(cls, args);
		return new Runnable()
		{
			@Override public void run()
			{	//próbuje wywołać dopasowany konstruktor
				try { Okno.this.okno = konstruktor.newInstance(args); }
				//wyjątki przechwycone ponieważ i tak trafiłyby do Swinga
				catch(Exception błąd)
					{ if(błądWykonania == null) błądWykonania = błąd; }
			}
		};
	}

	/**
	 * Dopasowuje konstruktor obiektu JFrame na podstawie argumentów
	 * wywołania konstruktora Okno().
	 * @param cls Class obiektu okna top-level
	 * @param args argumenty przekazywane do dopasowanego konstruktora
	 * @return konstruktor odpalający okno
	 * @throws NoSuchMethodException jeżeli nie można dopasować konstruktora
	 */
	private Constructor<? extends JFrame>
		dopasowanyKonstruktor(Class<? extends JFrame> cls, final Object[] args)
			throws NoSuchMethodException
	{
		Constructor<? extends JFrame> dopasowany = null;
		Class<?>[] typy = null;
		NoSuchMethodException ostatni = null;
		do	//dopasowuje argumenty do udanego dopasowania, albo powtórnie
			//wyrzuca ostatni wyjątek braku dopasowania
			if((typy = dobierajArgumenty(typy, args)) != null)
				try { dopasowany = cls.getConstructor(typy); }
				catch(NoSuchMethodException ex)
				{
					ostatni = ex;
					dopasowany = null;
				}
			else
				throw ostatni;
		while(dopasowany == null);
		return dopasowany;
	}

	/**
	 * Przekazuje listę typów (Class<?>) argumentów redukując wszystkie
	 * typy klas opakowujących typy proste na te typy proste - o ile
	 * argument poprzednie jest null lub reprezentuje tablicę pustą.
	 * W przeciwnym wypadku traktuje poprzednie jako wcześniej wygenerowaną
	 * tablicę typów Class<?>, w której zmienia jeden najbliższy obiekt
	 * Class typu prostego na typ Class obiektu opakowującego. Jeżeli nie
	 * można przeprowadzić takiej zmiany, to wynikiem jest bye.
	 * @param args tablica argumentów do dopasowania ich typów
	 * @param poprzednie tablica typów argumentów, której nie udało się
	 * dopasować wcześniej.
	 * @return lista typów prostych lub kolejno wymienianych na opakowujące
	 * lub null jeżeli już nic nie można wymienić.
	 */
	private Class<?>[] dobierajArgumenty(Class<?>[] poprzednie,
		Object[] argumenty)
	{	//najpierw eliminacja opakowań
		if(poprzednie == null || poprzednie.length == 0 )
		{	//wynikowa tablica typów argumentów
			Class<?>[] typy = new Class<?>[argumenty.length]; //pusta
			for(int i = 0; i < typy.length; ++i) //indeksy konieczne
				typy[i] = prosty(argumenty[i].getClass());
			return typy; //wszystkie opakowujące wymienione na proste
		}
		//teraz z powrotem stopniowa wymiana typów prostych na opakowania
		for(int i = 0; i < poprzednie.length; ++i) //indeksy konieczne
		{	//wynikiem jest "poprzednie" z jednym elementem
			Class<?> typ = opakowujący(poprzednie[i]);
			if(!typ.equals(poprzednie[i]))
			{	//wykrywana jest wyłącznie zmiana z prostego na opakowujący
				poprzednie[i] = typ;
				return poprzednie; //oddajemy wynik po jednej wymianie
			}
		}
		return null; //żadnych możliwych wymian do wykonania
	}

	/**
	 * @param typ dowolny
	 * @return opakowujący jeżeli argument jest typem prostym
	 */
	private Class<?> opakowujący(Class<?> typ)
	{
		for(int i = 0; i < proste.length; ++i)
			if(typ.equals(proste[i]))
				return opakowujące[i];
		return typ;
	}
	
	/**
	 * @param typ dowolny
	 * @return typ prosty jeżeli argument jest typem opakowującym
	 */
	private Class<?> prosty(Class<?> typ)
	{
		for(int i = 0; i < opakowujące.length; ++i)
			if(typ.equals(opakowujące[i]))
				return proste[i];
		return typ;
	}

	//tablice typów opakowujących i prostych muszą mieć te same rozmiary
	//oraz odpowiadające sobie nawzajem indeksy aby metody prosty i opakowujący
	//mogły prawidłowo działać.
	final private static Class<?>[] opakowujące =
	{
		Boolean.class, Character.class, Byte.class, Short.class,
		Integer.class, Long.class, Float.class, Double.class
	};

	final private static Class<?>[] proste =
	{
		boolean.class, char.class, byte.class, short.class,
		int.class, long.class, float.class, double.class
	};

	///pola///////////////////////////////////////////////////////////////////

	/* wymusza maksymalizację delegowanego okna */
	private boolean zmaksymalizowane = false;
	/* okno delegowane */
	protected JFrame okno = null;
	/* obiekt wyjątku gdy coś się sypnie */
	private Exception błądWykonania = null;
}
</cpp>