การใช้ NotNull Annotation ในวิธีการโต้แย้ง


156

ฉันเพิ่งเริ่มใช้@NotNullคำอธิบายประกอบกับ Java 8 และรับผลลัพธ์ที่ไม่คาดคิด

ฉันมีวิธีการเช่นนี้:

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

ฉันเขียนการทดสอบ JUnit ในค่า Null สำหรับอาร์กิวเมนต์ LookupList ฉันคาดหวังว่าจะเกิดข้อผิดพลาดบางประเภท แต่เกิดขึ้นราวกับว่าคำอธิบายประกอบไม่ได้อยู่ที่นั่น เป็นพฤติกรรมที่คาดหวังหรือไม่ จากสิ่งที่ฉันเข้าใจนี่คือเพื่อให้คุณข้ามการเขียนรหัสตรวจสอบ boilerplate null

คำอธิบายเกี่ยวกับสิ่งที่ @NotNull ที่ควรทำจะได้รับการชื่นชมอย่างมาก


29
@NotNullเป็นเพียงคำอธิบายประกอบ คำอธิบายประกอบไม่ได้ทำอะไรด้วยตัวเอง พวกเขาต้องการตัวประมวลผลคำอธิบายประกอบในเวลารวบรวมหรือสิ่งที่ประมวลผลได้ในขณะใช้งานจริง
Sotirios Delimanolis

คุณกำลังเรียกใช้รหัสภายในแอปพลิเคชันเซิร์ฟเวอร์ (ตัวอย่างเช่นการใช้Arquillian ) หรือไม่
jabu.10245

1
@SotiriosDelimanolis - ดังนั้นจุดคืออะไรเพียงเตือนให้ทุกคนที่เรียกวิธีการที่จะไม่ส่งผ่านค่า null? ในกรณีนี้คุณยังต้องใช้รหัสตรวจสอบตัวชี้โมฆะ
DavidR

1
ดูตัวตรวจสอบ Hibernate
arisalexis

@ jabu.10245 - ไม่ได้ใช้แอปพลิเคชันเซิร์ฟเวอร์ใด ๆ
DavidR

คำตอบ:


183

@Nullableและ@NotNullไม่ทำอะไรเลยด้วยตนเอง พวกเขาควรจะทำหน้าที่เป็นเครื่องมือเอกสาร

@Nullableหมายเหตุแจ้งเตือนคุณเกี่ยวกับความจำเป็นที่จะแนะนำการตรวจสอบ NPE เมื่อ:

  1. วิธีการโทรที่สามารถส่งคืน null
  2. การยกเลิกการพิจารณาตัวแปร (ฟิลด์, ตัวแปรโลคัล, พารามิเตอร์) ที่สามารถเป็นค่าว่างได้

@NotNullหมายเหตุเป็นจริงสัญญาอย่างชัดเจนประกาศต่อไปนี้:

  1. วิธีการไม่ควรกลับเป็นโมฆะ
  2. ตัวแปร (เช่นเขตข้อมูลตัวแปรท้องถิ่นและพารามิเตอร์) ไม่สามารถ ไม่ควร ถือค่า null

ตัวอย่างเช่นแทนที่จะเขียน:

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

คุณสามารถใช้ได้:

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

นอกจาก@NotNullนี้มักมีการตรวจสอบโดยConstraintValidators (เช่นในฤดูใบไม้ผลิและไฮเบอร์เนต)

@NotNullคำอธิบายประกอบไม่ได้ทำการตรวจสอบใด ๆ ในตัวของมันเองเพราะนิยามคำอธิบายประกอบไม่ได้ให้การใด ๆConstraintValidatorอ้างอิงประเภท

สำหรับข้อมูลเพิ่มเติมดู:

  1. การตรวจสอบถั่ว
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java

3
ดังนั้นเพียงชี้แจงส่วนที่ 2 ของส่วน NotNull จริง ๆ ควรพูดว่า "ไม่ควร" ไม่ใช่ "ไม่" เพราะไม่สามารถบังคับใช้เตียงได้ หรือถ้ามันสามารถบังคับใช้ในขณะทำงานคุณจะทำยังไงต่อ?
DavidR

ใช่มันเป็น "ไม่ควร" ... การใช้วิธีการควรบังคับใช้สัญญา
justAnotherUser ...

1
อีกวิธีหนึ่งใน Java 8 Optionalสามารถใช้แทน@Nullค่าที่ส่งคืนและเมธอดโอเวอร์โหลดแทนที่@Nullรายการพารามิเตอร์: dolszewski.com/java/java-8-optional-use-cases
Chad K

13
ผมเชื่อว่าความสับสนมาจาก doc จาวาคำอธิบายประกอบ NotNull นี้ * The annotated element must not be {@code null}. * Accepts any type.และฉันคิดว่าต้องคำจะถูกแทนที่ด้วยควรอีกครั้ง แต่มันขึ้นอยู่กับวิธีการที่คุณอ่านมัน คำอธิบายเพิ่มเติมบางอย่างแน่นอนจะเป็นการดี
Julian

@ จูเลียนฉันคิดว่าต้องเป็นคำที่เหมาะสมเพราะเป็นกฎไม่ใช่ข้อเสนอแนะ หากคุณใช้คำอธิบายประกอบที่ไม่ควรผ่านnullแต่จะได้รับอนุญาตคุณกำลังใช้คำอธิบายประกอบที่ไม่ถูกต้อง คำนี้ไม่ได้หมายความว่าได้รับการตรวจสอบแล้ว อย่างไรก็ตามคำใบ้ว่ามันไม่ผ่านการตรวจสอบจะไม่เจ็บ หากคุณต้องการเพิ่มการตรวจสอบอัตโนมัติคุณสามารถใช้เครื่องมือภายนอกบางอย่าง ตัวอย่างเช่นIntelliJ IDE ได้รับการสนับสนุนในตัวเพื่อฉีดการตรวจสอบค่าว่าง
JojOatXGME

27

ดังที่ได้กล่าวมา@NotNullแล้วไม่มีอะไรในตัวมันเอง วิธีที่ดีในการใช้@NotNullคือใช้กับObjects.requireNonNull

public class Foo {
    private final Bar bar;

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

6
เพียงแค่ปลาย นอกจากนี้คุณยังสามารถเขียนการมอบหมายดังกล่าวด้วยบรรทัดเดียว:this.bar = Objects.requireNonNull(bar, "bar must not be null");
lolung

ขอบคุณสำหรับคำแนะนำ @lolung - ตอนนี้ฉันได้อัปเดตโค้ดด้านบนแล้วตามความคิดเห็นของคุณ
บอลลีวูด


6

ดังนั้น @NotNull เพียงแค่เป็นแท็ก ... หากคุณต้องการตรวจสอบแล้วคุณต้องใช้สิ่งที่ต้องการตรวจสอบ hibernate jsr 303

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

ฉันจะวางสิ่งนี้ไว้ที่จุดเริ่มต้นของวิธีการได้อย่างไร
DavidR

ใช่ .. ที่จุดเริ่มต้นของวิธีการ ... นี่เป็นเพียงการใช้งานการตรวจสอบความถูกต้องอาจมีบางรายการที่ยัง ...
Naruto

ตกลง. แต่ความสำคัญนี้ของสิ่งที่รหัสนั้นจะไม่เปลี่ยนว่าฉันมีคำอธิบายประกอบ @NotNull ในอาร์กิวเมนต์ param หรือไม่
DavidR

ตอนนี้คุณมีการละเมิดทั้งหมดในชุดตรวจสอบขนาดถ้ามันมากกว่านั้นเป็นศูนย์แล้วกลับจากวิธีการ
นารูโตะ

3

หากคุณกำลังใช้ Spring คุณสามารถบังคับให้มีการตรวจสอบความถูกต้องโดยใส่คำอธิบายประกอบชั้นด้วย@Validated:

import org.springframework.validation.annotation.Validated;

มีข้อมูลเพิ่มเติมที่นี่: การตรวจสอบความถูกต้องของ Javax @NotNull การใช้คำอธิบายประกอบ


2

ฉันทำสิ่งนี้เพื่อสร้างคำอธิบายประกอบและการตรวจสอบความถูกต้องของตัวเอง:

ValidCardType.java(คำอธิบายประกอบเพื่อใส่วิธี / ฟิลด์)

@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 {};
}

และเครื่องมือตรวจสอบเพื่อทริกเกอร์การตรวจสอบ 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));
    }
}

@NotNullคุณสามารถทำบางสิ่งบางอย่างที่คล้ายกันมากในการตรวจสอบ


0

ในการทดสอบการตรวจสอบความถูกต้องของวิธีการในการทดสอบคุณจะต้องห่อหุ้มพร็อกซีในวิธีการ @Before

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

ด้วย MethodValidationProxyFactory เป็น:

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());
}

}

จากนั้นเพิ่มการทดสอบของคุณ:

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

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.