java/reflexe/src/cz/frantovo/příklady/reflexe/Reflexe.java
author František Kučera <franta-hg@frantovo.cz>
Sat, 20 Jan 2018 21:44:45 +0100
changeset 55 2fbf53cf0782
parent 36 03757ff74694
permissions -rw-r--r--
jvm-jni-starter: spouštění JVM (Java) z C/C++ přes JNI
franta-hg@27
     1
package cz.frantovo.příklady.reflexe;
franta-hg@27
     2
franta-hg@27
     3
import java.lang.reflect.Field;
franta-hg@27
     4
import java.math.BigDecimal;
franta-hg@27
     5
import java.math.RoundingMode;
franta-hg@36
     6
import java.util.Map;
franta-hg@36
     7
import java.util.Objects;
franta-hg@36
     8
import java.util.concurrent.ConcurrentHashMap;
franta-hg@27
     9
import java.util.function.Consumer;
franta-hg@27
    10
franta-hg@27
    11
/**
franta-hg@27
    12
 *
franta-hg@27
    13
 * @author Ing. František Kučera (frantovo.cz)
franta-hg@27
    14
 */
franta-hg@27
    15
public class Reflexe {
franta-hg@27
    16
franta-hg@27
    17
	private static final String NÁZEV_PROMĚNNÉ = "proměnná";
franta-hg@27
    18
	private static final int POČET_OPAKOVÁNÍ = 1000000;
franta-hg@27
    19
	private static final RoundingMode ZAOKROUHLOVÁNÍ = RoundingMode.UP;
franta-hg@27
    20
	private static final int PŘESNOST_PROCENT = 2;
franta-hg@27
    21
franta-hg@36
    22
	private static final Map<KlíčTřídaPole, Field> známáPole = new ConcurrentHashMap<>();
franta-hg@36
    23
	private static final Map<Class, Map<String, Field>> známáPole2 = new ConcurrentHashMap<>();
franta-hg@36
    24
franta-hg@27
    25
	private static void nastav(Object objekt, String proměnná, Object hodnota) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
franta-hg@27
    26
		Field f = objekt.getClass().getDeclaredField(proměnná);
franta-hg@27
    27
		f.setAccessible(true);
franta-hg@27
    28
		f.set(objekt, hodnota);
franta-hg@27
    29
		// TODO: můžeme odchytávat výjimky a vyhazovat vlastní výjimku případně běhovou
franta-hg@27
    30
	}
franta-hg@27
    31
franta-hg@36
    32
	private static void nastavOptimalizovaně(Object objekt, String proměnná, Object hodnota) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
franta-hg@36
    33
franta-hg@36
    34
		KlíčTřídaPole klíč = new KlíčTřídaPole(objekt.getClass(), proměnná);
franta-hg@36
    35
		Field f = známáPole.get(klíč);
franta-hg@36
    36
franta-hg@36
    37
		if (f == null) {
franta-hg@36
    38
			f = objekt.getClass().getDeclaredField(proměnná);
franta-hg@36
    39
			f.setAccessible(true);
franta-hg@36
    40
			známáPole.put(klíč, f);
franta-hg@36
    41
		}
franta-hg@36
    42
franta-hg@36
    43
		f.set(objekt, hodnota);
franta-hg@36
    44
		// TODO: můžeme odchytávat výjimky a vyhazovat vlastní výjimku případně běhovou
franta-hg@36
    45
	}
franta-hg@36
    46
franta-hg@36
    47
	private static class KlíčTřídaPole {
franta-hg@36
    48
franta-hg@36
    49
		private final Class třída;
franta-hg@36
    50
		private final String pole;
franta-hg@36
    51
franta-hg@36
    52
		public KlíčTřídaPole(Class třída, String pole) {
franta-hg@36
    53
			this.třída = třída;
franta-hg@36
    54
			this.pole = pole;
franta-hg@36
    55
		}
franta-hg@36
    56
franta-hg@36
    57
		@Override
franta-hg@36
    58
		public boolean equals(Object obj) {
franta-hg@36
    59
			if (obj instanceof KlíčTřídaPole) {
franta-hg@36
    60
				KlíčTřídaPole druhýKlíč = (KlíčTřídaPole) obj;
franta-hg@36
    61
				return Objects.equals(třída, druhýKlíč.třída) && Objects.equals(pole, druhýKlíč.pole);
franta-hg@36
    62
			} else {
franta-hg@36
    63
				return false;
franta-hg@36
    64
			}
franta-hg@36
    65
		}
franta-hg@36
    66
franta-hg@36
    67
		@Override
franta-hg@36
    68
		public int hashCode() {
franta-hg@36
    69
			return Objects.hash(třída, pole);
franta-hg@36
    70
		}
franta-hg@36
    71
franta-hg@36
    72
	}
franta-hg@36
    73
franta-hg@36
    74
	private static void nastavOptimalizovaně2(Object objekt, String proměnná, Object hodnota) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
franta-hg@36
    75
franta-hg@36
    76
		Map<String, Field> mapaPolí = známáPole2.get(objekt.getClass());
franta-hg@36
    77
		if (mapaPolí == null) {
franta-hg@36
    78
			mapaPolí = new ConcurrentHashMap<>();
franta-hg@36
    79
			známáPole2.put(objekt.getClass(), mapaPolí);
franta-hg@36
    80
		}
franta-hg@36
    81
franta-hg@36
    82
		Field f = mapaPolí.get(proměnná);
franta-hg@36
    83
		if (f == null) {
franta-hg@36
    84
			f = objekt.getClass().getDeclaredField(proměnná);
franta-hg@36
    85
			f.setAccessible(true);
franta-hg@36
    86
			mapaPolí.put(proměnná, f);
franta-hg@36
    87
		}
franta-hg@36
    88
franta-hg@36
    89
		f.set(objekt, hodnota);
franta-hg@36
    90
		// TODO: můžeme odchytávat výjimky a vyhazovat vlastní výjimku případně běhovou
franta-hg@36
    91
	}
franta-hg@36
    92
franta-hg@27
    93
	private static void testSetter(int početOpakování) {
franta-hg@27
    94
		NějakáTřída t = new NějakáTřída();
franta-hg@27
    95
		for (int i = 0; i < početOpakování; i++) {
franta-hg@27
    96
			t.setProměnná(i);
franta-hg@27
    97
		}
franta-hg@27
    98
	}
franta-hg@27
    99
franta-hg@27
   100
	private static void testPřímýPřístup(int početOpakování) {
franta-hg@27
   101
		NějakáTřída t = new NějakáTřída();
franta-hg@27
   102
		for (int i = 0; i < početOpakování; i++) {
franta-hg@27
   103
			t.veřejnáProměnná = i;
franta-hg@27
   104
		}
franta-hg@27
   105
	}
franta-hg@27
   106
franta-hg@27
   107
	private static void testReflexe(int početOpakování) {
franta-hg@27
   108
		NějakáTřída t = new NějakáTřída();
franta-hg@27
   109
		try {
franta-hg@27
   110
			for (int i = 0; i < početOpakování; i++) {
franta-hg@27
   111
				nastav(t, NÁZEV_PROMĚNNÉ, i);
franta-hg@27
   112
			}
franta-hg@27
   113
		} catch (Exception e) {
franta-hg@27
   114
			throw new RuntimeException("test se nezdařil", e);
franta-hg@27
   115
		}
franta-hg@27
   116
	}
franta-hg@27
   117
franta-hg@36
   118
	private static void testReflexeOptimalizovaná(int početOpakování) {
franta-hg@36
   119
		NějakáTřída t = new NějakáTřída();
franta-hg@36
   120
		try {
franta-hg@36
   121
			for (int i = 0; i < početOpakování; i++) {
franta-hg@36
   122
				nastavOptimalizovaně(t, NÁZEV_PROMĚNNÉ, i);
franta-hg@36
   123
			}
franta-hg@36
   124
		} catch (Exception e) {
franta-hg@36
   125
			throw new RuntimeException("test se nezdařil", e);
franta-hg@36
   126
		}
franta-hg@36
   127
	}
franta-hg@36
   128
franta-hg@36
   129
	private static void testReflexeOptimalizovaná2(int početOpakování) {
franta-hg@36
   130
		NějakáTřída t = new NějakáTřída();
franta-hg@36
   131
		try {
franta-hg@36
   132
			for (int i = 0; i < početOpakování; i++) {
franta-hg@36
   133
				nastavOptimalizovaně2(t, NÁZEV_PROMĚNNÉ, i);
franta-hg@36
   134
			}
franta-hg@36
   135
		} catch (Exception e) {
franta-hg@36
   136
			throw new RuntimeException("test se nezdařil", e);
franta-hg@36
   137
		}
franta-hg@36
   138
	}
franta-hg@36
   139
franta-hg@27
   140
	private static void testReflexePřipravená(int početOpakování) {
franta-hg@27
   141
		NějakáTřída t = new NějakáTřída();
franta-hg@27
   142
		try {
franta-hg@27
   143
			Field f = t.getClass().getDeclaredField(NÁZEV_PROMĚNNÉ);
franta-hg@27
   144
			f.setAccessible(true);
franta-hg@27
   145
			for (int i = 0; i < početOpakování; i++) {
franta-hg@27
   146
				f.set(t, i);
franta-hg@27
   147
			}
franta-hg@27
   148
		} catch (Exception e) {
franta-hg@27
   149
			throw new RuntimeException("test se nezdařil", e);
franta-hg@27
   150
		}
franta-hg@27
   151
	}
franta-hg@27
   152
franta-hg@27
   153
	private static String lpad(int početZnaků, Object text) {
franta-hg@27
   154
		return String.format("%1$" + početZnaků + "s", String.valueOf(text));
franta-hg@27
   155
	}
franta-hg@27
   156
franta-hg@27
   157
	private static BigDecimal spočítejProcenta(long hodnota, long základ) {
franta-hg@27
   158
		BigDecimal h = BigDecimal.valueOf(hodnota);
franta-hg@27
   159
		BigDecimal z = BigDecimal.valueOf(základ);
franta-hg@27
   160
		return h.multiply(BigDecimal.valueOf(100)).divide(z, PŘESNOST_PROCENT, ZAOKROUHLOVÁNÍ);
franta-hg@27
   161
	}
franta-hg@27
   162
franta-hg@27
   163
	private static Long testuj(String názevTestu, int početOpakování, Long časProPorovnání, Consumer<Integer> test) {
franta-hg@36
   164
		System.out.print("TEST: " + lpad(24, názevTestu) + ": ");
franta-hg@27
   165
		try {
franta-hg@27
   166
			long začátek = System.currentTimeMillis();
franta-hg@27
   167
			test.accept(početOpakování);
franta-hg@27
   168
			long konec = System.currentTimeMillis();
franta-hg@27
   169
			long celkovýČas = konec - začátek;
franta-hg@27
   170
			BigDecimal relativníČas = časProPorovnání == null ? BigDecimal.valueOf(100).setScale(PŘESNOST_PROCENT) : spočítejProcenta(celkovýČas, časProPorovnání);
franta-hg@27
   171
			System.out.println("početOpakování = " + lpad(12, početOpakování) + " celkovýČas = " + lpad(8, celkovýČas) + " ms relativníČas = " + lpad(8, relativníČas) + " %");
franta-hg@27
   172
			return celkovýČas;
franta-hg@27
   173
		} catch (Exception e) {
franta-hg@27
   174
			System.out.println("došlo k chybě");
franta-hg@27
   175
			e.printStackTrace();
franta-hg@27
   176
			return null;
franta-hg@27
   177
		}
franta-hg@27
   178
	}
franta-hg@27
   179
franta-hg@27
   180
	public static void main(String[] args) {
franta-hg@27
   181
		long základníČas = testuj("přímý přístup", POČET_OPAKOVÁNÍ, null, Reflexe::testPřímýPřístup);
franta-hg@27
   182
		testuj("setter", POČET_OPAKOVÁNÍ, základníČas, Reflexe::testSetter);
franta-hg@27
   183
		testuj("reflexe", POČET_OPAKOVÁNÍ, základníČas, Reflexe::testReflexe);
franta-hg@36
   184
		testuj("reflexe optimalizovaná 1", POČET_OPAKOVÁNÍ, základníČas, Reflexe::testReflexeOptimalizovaná);
franta-hg@36
   185
		testuj("reflexe optimalizovaná 2", POČET_OPAKOVÁNÍ, základníČas, Reflexe::testReflexeOptimalizovaná2);
franta-hg@27
   186
		testuj("reflexe připravená", POČET_OPAKOVÁNÍ, základníČas, Reflexe::testReflexePřipravená);
franta-hg@27
   187
	}
franta-hg@27
   188
franta-hg@36
   189
}