Есть библиотека которая использует HashMap
из стандартной библиотеки. Как можно сделать так, чтобы она использовала мой HashMap
? Код библиотеки трогать нельзя.
Есть несколько разных способов, а сама ситуация довольно тривиальна. У вас нет возможности получить исходники и собрать вручную новый jar-ник, а обычно никто просто не хочет сам пересобирать проект, подключать его как модуль или как-то ещё терять возможность использовать зависимость, прописав одну строчку в конфигурационном файле. Один из способов я опишу ниже, им в случае крайней необходимости пользуюсь сам и, по моему скромному мнению, он не выглядит как костыль.
Способ заключается в простом: если класс не final
и от него можно отнаследоваться таким образом, чтобы можно было безболезненно изменить все нужные части и иметь возможность дальше прокидывать «ребёнка» под видом инстанса самого класса, то так и стоит поступить. Просто экстендимся, переопределяем все нужные части и так далее. Если поля private
, или всё жёстко друг с другом связано — можно перейти к пункту 2. Но нередко и такой способ может помочь. В этом случае вы не теряете возможность получения обновлений используемой библиотеки, не лезете в её код, не пользуетесь рефлексией и прочими радостями жизни Java-разработчика.
Второй способ чуть сложнее. Логично, что даже если вы назовёте класс также, прокинуть его под видом класса библиотеки не получится, и вы наткнётесь, что встроить свою реализацию нужного класса не получится просто так. В этом случае вы просто копируете нужный класс, производите нужные манипуляции (меняете тип полей, входные параметры и вообще любую логику) и берёте все классы, которые жёстко привязывают к использованию изменённого класса. Переписываете и их, меняя лишь тип параметра в нужных местах. Если библиотека написана нормально, то уровень абстракции должен позволить поменять не более пары-тройки классов кроме непосредственно самого, который вы меняли. И то, в них придётся поменять по паре строк кода.
Мне приходилось заниматься подобным не раз, когда хотелось встроить свою сложную или специфичную логику в код библиотеки, но это нельзя было сделать просто прокинув куда-то свой класс, имплементящий нужный интерфейс, а клонить её всю и менять её сорцы — последнее, к чему я бы прибегнул, особенно, если библиотека немаленькая и часто обновляется.
Если HashMap создаются как локальные переменные методов:
Здесь можно воспользоваться Java Agent
public class HashMapTransformationAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("I am javaagent Counter");
instrumentation.addTransformer(new HashMapClassTransformer());
}
}
Полезная статья про Java Agent https://habr.com/post/230239/
"Он будет запущен еще перед запуском вашего приложения. Сам агент это отдельное приложение которое предоставляет доступ к механизму манипуляции байт-кодом (java.lang.instrument) в runtime." В ClassFileTransformer можно использовать библиотеки Javaassist или Java ASM. Java ASM более низкоуровневая http://www.baeldung.com/java-asm.
Пример с Javaassist, замеряем время работы метода put.
public class HashMapClassTransformer implements ClassFileTransformer {
private static int count = 0;
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("load class: " + className.replaceAll("/", "."));
System.out.println(String.format("loaded %s classes", ++count));
String ctClassName = className.replaceAll("/", ".");
try {
if(ctClassName.equals("java.util.HashMap")) {
// Javassist
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(ctClassName);
CtMethod m = cc.getDeclaredMethod("put");
m.addLocalVariable("elapsedTime", CtClass.longType);
m.insertBefore(
"elapsedTime = System.currentTimeMillis();"
//+ "long a = 5000l;"
+ "Thread.sleep(1000l);"
);
m.getMethodInfo().getAttributes();
m.insertAfter(
"{elapsedTime = System.currentTimeMillis() - elapsedTime;"
+ "System.out.println(\"Method Executed in ms: \" + elapsedTime);}"
);
byte[] byteCode = cc.toBytecode();
cc.detach();
return byteCode;
} catch (Exception ex) {
ex.printStackTrace();
}
}
} catch (Exception e){
System.out.println("Error: " + e.getMessage());
}
return classfileBuffer;
}
}
Но минус данного подхода в том, что теперь весь ваш код, а не только сторонняя библиотека, будет использовать кастомный HashMap.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
В строке присвоения значения rootdata возникает ошибка NullPointerExeption, не могу понять почему
Проблема: в эмуляторе студии margin работает, а вот на планшете нет, в чем может быть заковырка?
Мне нужно узнать "data-key" элемента в таблице("Some name" на скриншоте) который завернут в тег "td"Можно это сделать используя Selenium WD + Java?