Я собираюсь совершить уродливый временный взлом, чтобы обойти проблему с блокировкой, пока мы ждем, пока внешний ресурс будет исправлен. Помимо того, чтобы отметить это большим пугающим комментарием и кучей FIXME, я бы хотел, чтобы компилятор выдавал очевидное предупреждающее сообщение в качестве напоминания, чтобы мы не забыли удалить это. Например, что-то вроде:
[javac] com.foo.Hacky.java:192: warning: FIXME temporary hack to work around library bug, remove me when library is fixed!
Есть ли способ вызвать преднамеренное предупреждение компилятора с выбранным мной сообщением? В противном случае, что проще всего добавить в код, чтобы выдать существующее предупреждение, возможно, с сообщением в строке в строке с нарушением, чтобы оно было напечатано в предупреждающем сообщении?
EDIT: устаревшие теги, похоже, ничего не делают для меня:
/**
* @deprecated "Temporary hack to work around remote server quirks"
*/
@Deprecated
private void doSomeHackyStuff() { ... }
Никаких ошибок компилятора или времени выполнения в eclipse или из sun javac 1.6 (выполняется из сценария ant), и он определенно выполняет функцию.
источник
Ответы:
Я видел, как использовался один метод - связать это с модульным тестированием (вы ведь проводите модульное тестирование, верно?). По сути, вы создаете модульный тест, который завершается ошибкой после достижения исправления внешнего ресурса. Затем вы комментируете этот модульный тест, чтобы рассказать другим, как отменить ваш грубый взлом, как только проблема будет решена.
Что действительно хорошо в этом подходе, так это то, что спусковым крючком для отмены вашего взлома является устранение самой основной проблемы.
источник
Думаю, выходом будет специальная аннотация, которая будет обработана компилятором. Я часто пишу собственные аннотации, чтобы делать что-то во время выполнения, но никогда не пробовал использовать их во время компиляции. Итак, я могу дать вам только указатели на инструменты, которые могут вам понадобиться:
Я не знаю, реально ли это решение. Попробую реализовать сам, когда найду время.
редактировать
Я успешно реализовал свое решение. И в качестве бонуса я использовал возможность поставщика услуг Java, чтобы упростить его использование. Фактически, мое решение - это банка, содержащая 2 класса: пользовательскую аннотацию и обработчик аннотаций. Чтобы использовать его, просто добавьте эту банку в путь к классам вашего проекта и аннотируйте все, что хотите! Это нормально работает в моей среде IDE (NetBeans).
Код аннотации:
package fr.barjak.hack; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE}) public @interface Hack { }
Код процессора:
package fr.barjak.hack_processor; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; @SupportedAnnotationTypes("fr.barjak.hack.Hack") public class Processor extends AbstractProcessor { private ProcessingEnvironment env; @Override public synchronized void init(ProcessingEnvironment pe) { this.env = pe; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { for (TypeElement te : annotations) { final Set< ? extends Element> elts = roundEnv.getElementsAnnotatedWith(te); for (Element elt : elts) { env.getMessager().printMessage(Kind.WARNING, String.format("%s : thou shalt not hack %s", roundEnv.getRootElements(), elt), elt); } } } return true; } }
Чтобы включить полученную банку в качестве поставщика услуг, добавьте файл
META-INF/services/javax.annotation.processing.Processor
в банку. Этот файл представляет собой файл acsii, который должен содержать следующий текст:источник
init
и устанавливатьenv
поле - вы можете получитьProcessingEnvironment
от,this.processingEnv
поскольку оноprotected
.Некоторый быстрый и не такой грязный подход может заключаться в использовании
@SuppressWarnings
аннотации с заведомо неправильнымString
аргументом:@SuppressWarnings("FIXME: this is a hack and should be fixed.")
Это вызовет предупреждение, поскольку компилятор не распознает его как конкретное предупреждение, которое необходимо подавить:
источник
Один хороший хакер заслуживает другого ... Я обычно генерирую предупреждения компилятора для описанной цели, вводя неиспользуемую переменную в хакерский метод, таким образом:
/** * @deprecated "Temporary hack to work around remote server quirks" */ @Deprecated private void doSomeHackyStuff() { int FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed; ... }
Эта неиспользуемая переменная выдаст предупреждение, которое (в зависимости от вашего компилятора) будет выглядеть примерно так:
Это решение не так хорошо, как пользовательская аннотация, но имеет то преимущество, что не требует предварительной подготовки (при условии, что компилятор уже настроен на выдачу предупреждений о неиспользуемых переменных). Я бы предположил, что этот подход подходит только для краткосрочных взломов. Я бы сказал, что для долгоживущих хаков оправдано усилия по созданию пользовательской аннотации.
источник
build.gradle
?Я написал библиотеку, которая делает это с аннотациями: Lightweight Javac @Warning Annotation
Использование очень простое:
// some code... @Warning("This method should be refactored") public void someCodeWhichYouNeedAtTheMomentButYouWantToRefactorItLater() { // bad stuff going on here... }
И компилятор выдаст предупреждающее сообщение с вашим текстом
источник
как насчет того, чтобы пометить метод или класс как @Deprecated? документы здесь . Обратите внимание, что есть как @Deprecated, так и @deprecated - заглавная версия D - это аннотация, а строчная d - версия javadoc. Версия javadoc позволяет вам указать произвольную строку, объясняющую, что происходит. Но компиляторы не обязаны выдавать предупреждение при его просмотре (хотя многие это делают). Аннотации всегда должны вызывать предупреждение, хотя я не думаю, что вы можете добавить к ней пояснения.
ОБНОВЛЕНИЕ вот код, который я только что тестировал: Sample.java содержит:
public class Sample { @Deprecated public static void foo() { System.out.println("I am a hack"); } }
SampleCaller.java содержит:
public class SampleCaller{ public static void main(String [] args) { Sample.foo(); } }
когда я запускаю "javac Sample.java SampleCaller.java", я получаю следующий результат:
Note: SampleCaller.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details.
Я использую Sun javac 1.6. Если вы хотите получить честное предупреждение, а не просто примечание, используйте параметр -Xlint. Возможно, это должным образом проникнет в Ant.
источник
@Deprecated
работает только между классами (поэтому он бесполезен для частных методов).Мы можем сделать это с помощью аннотаций!
Чтобы вызвать ошибку, используйте
Messager
для отправки сообщения с помощьюDiagnostic.Kind.ERROR
. Краткий пример:processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, "Something happened!", element);
Вот довольно простая аннотация, которую я написал, чтобы проверить это.
Эта
@Marker
аннотация указывает на то, что целью является интерфейс маркера:package marker; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Marker { }
И обработчик аннотаций вызывает ошибку, если это не так:
package marker; import javax.annotation.processing.*; import javax.lang.model.*; import javax.lang.model.element.*; import javax.lang.model.type.*; import javax.lang.model.util.*; import javax.tools.Diagnostic; import java.util.Set; @SupportedAnnotationTypes("marker.Marker") @SupportedSourceVersion(SourceVersion.RELEASE_6) public final class MarkerProcessor extends AbstractProcessor { private void causeError(String message, Element e) { processingEnv.getMessager() .printMessage(Diagnostic.Kind.ERROR, message, e); } private void causeError( Element subtype, Element supertype, Element method) { String message; if (subtype == supertype) { message = String.format( "@Marker target %s declares a method %s", subtype, method); } else { message = String.format( "@Marker target %s has a superinterface " + "%s which declares a method %s", subtype, supertype, method); } causeError(message, subtype); } @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Elements elementUtils = processingEnv.getElementUtils(); boolean processMarker = annotations.contains( elementUtils.getTypeElement(Marker.class.getName())); if (!processMarker) return false; for (Element e : roundEnv.getElementsAnnotatedWith(Marker.class)) { ElementKind kind = e.getKind(); if (kind != ElementKind.INTERFACE) { causeError(String.format( "target of @Marker %s is not an interface", e), e); continue; } if (kind == ElementKind.ANNOTATION_TYPE) { causeError(String.format( "target of @Marker %s is an annotation", e), e); continue; } ensureNoMethodsDeclared(e, e); } return true; } private void ensureNoMethodsDeclared( Element subtype, Element supertype) { TypeElement type = (TypeElement) supertype; for (Element member : type.getEnclosedElements()) { if (member.getKind() != ElementKind.METHOD) continue; if (member.getModifiers().contains(Modifier.STATIC)) continue; causeError(subtype, supertype, member); } Types typeUtils = processingEnv.getTypeUtils(); for (TypeMirror face : type.getInterfaces()) { ensureNoMethodsDeclared(subtype, typeUtils.asElement(face)); } } }
Например, это правильное использование
@Marker
:@Marker interface Example {}
@Marker interface Example extends Serializable {}
Но такое использование
@Marker
вызовет ошибку компилятора:@Marker class Example {}
@Marker interface Example { void method(); }
Вот запись в блоге, которую я нашел очень полезной для начала работы по этой теме:
Небольшое примечание: комментатор ниже указывает на то, что из-за
MarkerProcessor
ссылокMarker.class
он зависит от него во время компиляции. Я написал приведенный выше пример с предположением, что оба класса будут находиться в одном файле JAR (скажем,marker.jar
), но это не всегда возможно.Например, предположим, что существует JAR приложения со следующими классами:
com.acme.app.Main com.acme.app.@Ann com.acme.app.AnnotatedTypeA (uses @Ann) com.acme.app.AnnotatedTypeB (uses @Ann)
Тогда процессор для
@Ann
существует в отдельном JAR, который используется при компиляции JAR приложения:com.acme.proc.AnnProcessor (processes @Ann)
В этом случае
AnnProcessor
невозможно будет ссылаться на тип@Ann
напрямую, потому что это создаст циклическую зависимость JAR. Можно будет ссылаться только@Ann
поString
имени илиTypeElement
/TypeMirror
.источник
Set<? extends TypeElement>
параметра, а затем получаете аннотированные элементы для данного раунда с помощьюgetElementsAnnotatedWith(TypeElement annotation)
. Я тоже не понимаю, зачем вы завернулиprintMessage
метод.Здесь показано руководство по аннотациям, а внизу приведен пример определения ваших собственных аннотаций. К сожалению, беглый просмотр учебника показал, что они доступны только в javadoc ...
Таким образом, похоже, что все, что вы действительно можете сделать, это добавить тег @Deprecated, который компилятор распечатает, или поместит настраиваемый тег в javadocs, который сообщает о взломе.
источник
Если вы используете IntelliJ. Вы можете перейти в: «Настройки»> «Редактор»> «TODO» и добавить «\ bhack.b *» или любой другой шаблон.
Если вы затем сделаете комментарий вроде
// HACK: temporary fix to work around server issues
Затем в окне инструмента TODO он будет красиво отображаться вместе со всеми другими заданными вами шаблонами во время редактирования.
источник
Вы должны использовать инструмент для компиляции, например ant ou maven. С его помощью вы должны определить некоторые задачи во время компиляции, которые могут создавать журналы (например, сообщения или предупреждения) о ваших тегах FIXME.
И если вам нужны ошибки, это тоже возможно. Например, остановить компиляцию, когда вы оставили в коде несколько TODO (почему бы и нет?)
источник
Чтобы вообще появилось какое-либо предупреждение, я обнаружил, что неиспользуемые переменные и пользовательские @SuppressWarnings у меня не работают, но работает ненужное приведение:
public class Example { public void warn() { String fixmePlease = (String)"Hello"; } }
Теперь, когда я компилирую:
$ javac -Xlint:all Example.java ExampleTest.java:12: warning: [cast] redundant cast to String String s = (String) "Hello!"; ^ 1 warning
источник