Menggunakan NotNull Annotation dalam argumen metode


156

Saya baru saja mulai menggunakan @NotNullanotasi dengan Java 8 dan mendapatkan beberapa hasil yang tidak terduga.

Saya punya metode seperti ini:

public List<Found> findStuff(@NotNull List<Searching> searchingList) {
    ... code here ...
}

Saya menulis tes JUnit yang meneruskan nilai nol untuk argumen searchingList. Saya mengharapkan beberapa jenis kesalahan terjadi tetapi itu berlalu seolah-olah penjelasan itu tidak ada. Apakah ini perilaku yang diharapkan? Dari apa yang saya mengerti, ini adalah untuk memungkinkan Anda melewati penulisan kode cek nol boilerplate.

Penjelasan tentang apa yang seharusnya dilakukan oleh @NotNull akan sangat dihargai.


29
@NotNullhanyalah anotasi. Anotasi tidak melakukan apa-apa sendiri. Mereka memerlukan prosesor anotasi pada waktu kompilasi, atau sesuatu yang memprosesnya saat runtime.
Sotirios Delimanolis

Apakah Anda menjalankan kode di dalam server aplikasi (misalnya menggunakan Arquillian )?
jabu.10245

1
@ SotiriosDelimanolis - Jadi apa gunanya, hanya peringatan bagi siapa saja yang memanggil metode untuk tidak melewatkan nilai nol? Dalam hal ini Anda masih memerlukan kode validasi penunjuk nol.
DavidR

1
lihat validator hibernate
arisalexis

@ jabu.10245 - Tidak menggunakan server aplikasi apa pun.
DavidR

Jawaban:


183

@Nullabledan @NotNulltidak melakukan apa-apa sendiri. Mereka seharusnya bertindak sebagai alat Dokumentasi.

The @NullableAnotasi mengingatkan Anda tentang perlunya untuk memperkenalkan pemeriksaan NPE saat:

  1. Metode panggilan yang dapat mengembalikan nol.
  2. Variabel penereferensi (bidang, variabel lokal, parameter) yang dapat menjadi nol.

The @NotNullAnotasi adalah, sebenarnya, kontrak eksplisit menyatakan berikut ini:

  1. Metode tidak boleh mengembalikan null.
  2. Variabel (seperti bidang, variabel lokal, dan parameter) tidak boleh tidak memiliki nilai nol.

Misalnya, alih-alih menulis:

/**
 * @param aX should not be null
 */
public void setX(final Object aX ) {
    // some code
}

Kamu bisa memakai:

public void setX(@NotNull final Object aX ) {
    // some code
}

Selain itu, @NotNullsering diperiksa oleh ConstraintValidators (mis. Pada musim semi dan hibernasi).

The @NotNullpenjelasan tidak melakukan validasi setiap sendiri karena definisi penjelasan tidak menyediakan ConstraintValidatorreferensi jenis.

Untuk info lebih lanjut, lihat:

  1. Validasi kacang
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java

3
Jadi hanya untuk memperjelas bagian 2 dari bagian NotNull, benar-benar harus mengatakan "tidak boleh", bukan "tidak bisa" karena tidak dapat diterapkan? Atau jika itu bisa diberlakukan pada saat runtime, bagaimana Anda melakukannya?
DavidR

Ya, itu "tidak boleh" ... implementasi metode harus menegakkan kontrak.
justAnotherUser ...

1
Atau, di Java 8, Optionaldapat digunakan sebagai pengganti @Nullnilai pengembalian, dan metode kelebihan beban di @Nulldalam daftar parameter: dolszewski.com/java/java-8-optional-use-cases
Chad K

13
Saya percaya kebingungan berasal dari dokumen java dari penjelasan NotNull: * The annotated element must not be {@code null}. * Accepts any type.dan saya pikir harus kata harus diganti dengan harus tetapi sekali lagi itu tergantung bagaimana Anda membacanya. Pasti ada beberapa klarifikasi yang lebih baik untuk dimiliki
Julian

@Julian Saya pikir pasti adalah istilah yang tepat karena itu adalah aturan, bukan rekomendasi. Jika Anda menggunakan anotasi yang tidak boleh Anda lewati nulltetapi diizinkan, Anda menggunakan anotasi yang salah. Istilah ini tidak menyiratkan bahwa itu divalidasi. Namun, petunjuk bahwa itu tidak divalidasi tidak akan sakit. Jika Anda ingin menambahkan validasi otomatis, Anda dapat menggunakan beberapa alat eksternal. Misalnya, IntelliJ IDE telah membangun dukungan untuk menyuntikkan cek-kosong.
JojOatXGME

27

Seperti disebutkan di atas @NotNulltidak melakukan apa-apa sendiri. Cara yang baik untuk menggunakannya @NotNulladalah menggunakannyaObjects.requireNonNull

public class Foo {
    private final Bar bar;

    public Foo(@NotNull Bar bar) {
        this.bar = Objects.requireNonNull(bar, "bar must not be null");
    }
}

6
Hanya sebuah tip. Anda juga dapat menulis tugas seperti itu dengan satu baris:this.bar = Objects.requireNonNull(bar, "bar must not be null");
lolung

Terima kasih atas tip @lolung - Saya sekarang telah memperbarui kode di atas yang dipotong berdasarkan komentar Anda.
Bollywood


6

SO @NotNull hanyalah sebuah tag ... Jika Anda ingin memvalidasinya, maka Anda harus menggunakan sesuatu seperti hibernate validator jsr 303

ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
 Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);

Di mana saya meletakkan ini, di awal metode?
DavidR

ya..di awal metode ... ini hanya salah satu implementasi validasi, mungkin ada yang lain juga ...
Naruto

Baik. Tetapi signifikansi apa yang dilakukan kode ini tidak akan mengubah apakah saya memiliki anotasi @NotNull atau tidak dalam argumen param?
DavidR

Sekarang Anda memiliki semua Pelanggaran di set, periksa ukurannya, jika lebih besar dari nol, lalu kembali dari metode.
Naruto


2

Saya melakukan ini untuk membuat anotasi dan validator validasi saya sendiri:

ValidCardType.java(penjelasan untuk memakai metode / bidang)

@Constraint(validatedBy = {CardTypeValidator.class})
@Documented
@Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidCardType {
    String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Dan, validator memicu cek CardTypeValidator.java::

public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
    private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};

    @Override
    public void initialize(ValidCardType status) {
    }
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (Arrays.asList(ALL_CARD_TYPES).contains(value));
    }
}

Anda dapat melakukan sesuatu yang sangat mirip untuk diperiksa @NotNull.


0

Untuk menguji validasi metode Anda dalam pengujian, Anda harus membungkusnya dengan proxy dalam metode @Before.

@Before
public void setUp() {
    this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}

Dengan MethodValidationProxyFactory sebagai:

import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

public class MethodValidationProxyFactory {

private static final StaticApplicationContext ctx = new StaticApplicationContext();

static {
    MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
    processor.afterPropertiesSet(); // init advisor
    ctx.getBeanFactory()
            .addBeanPostProcessor(processor);
}

@SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {

    return (T) ctx.getAutowireCapableBeanFactory()
            .applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
                    .getName());
}

}

Dan kemudian, tambahkan tes Anda:

@Test
public void findingNullStuff() {
 assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.