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