2016-07-29 7 views
7

Скажем, у меня есть класс с несколькими конструкторами, один из которых является копией-конструктор (для копирования объекта):Java нулевые аргументы, когда цепные Конструкторы

public class Rectangle { 

    int width, height; 

    public Rectangle(int width, int height) { 
     this.width = width; 
     this.height = height; 
    } 

    public Rectangle(Rectangle source) { 
     this(source.width, source.height); 
    } 
} 

Есть ли способ, что я могу сделать чек, если source - null в копировальном конструкторе и бросать IllegalArgumentException, если это так? Потому что другой вызов конструктора имеет, чтобы быть первым утверждением в моем конструкторе.

+0

Почему второй вызов конструктора должен быть первым выражением в конструкторе копирования? – Janno

+2

Потому что это то, чего хочет Java. – kalsowerus

+5

@ Джанно: Потому что так работает Java. Вы не можете использовать 'this (...)' после другого оператора. –

ответ

12

Вы можете сделать это:

public Rectangle(Rectangle source) { 
    this(checkNotNull(source, "Source cannot be null").width, source.height); 
} 

private static <T> T checkNotNull(T t, String msg) { 
    if (t == null) throw new IllegalArgumentException(msg); 
    return t; 
} 

Я также согласен с Джоном Скит, что NullPointerException не плохой bevahiour в этом случае. Единственное, что в длинных очередях, когда вы получаете NPE, может быть немного сложно определить, какой объект null, поэтому более конкретное сообщение может быть полезным.

Вы можете также не изобретать велосипед и использовать стандартные java.util.Objects методы, если вы не потрудились бросать NullPointerException вместо:

public Rectangle(Rectangle source) { 
    this(Objects.requireNonNull(source, "Source cannot be null").width, source.height); 
} 

если сообщение об ошибке дорого строить, вы можете предоставить Supplier<String> вместо этого, оплатить стоимость строительства сообщения только тогда, когда это действительно необходимо:

public Rectangle(Rectangle source) { 
    this(Objects.requireNonNull(source,() -> explainError(source)).width, source.height); 
} 
+0

Метод checkNotNull должен возвращать значение типа 'T'. – Omkar

+0

@Omkar ups, thanks – Dici

7

Да, вы можете использовать вспомогательный метод, который будет бросать исключение, если это необходимо, и вернуть исходное значение OTHE rwise ... вы можете вызвать это внутри своего вызова конструктора, так как вы разрешаете вызовы метода как часть оценки аргументов.

// In a helper class 
public static <T> T checkNotNull(T value) { 
    if (value == null) { 
     throw new IllegalArgumentException(); 
    } 
    return value; 
} 

Затем используйте его как:

public Rectangle(Rectangle source) { 
    this(Helper.checkNotNull(source).width, source.height); 
} 

Однако ... Я считаю, что NullPointerException является рекомендуемым исключение бросить здесь в любом случае (в Эффективное Java 2-е издание, к примеру), что существующий код будет бросать уже. Так что вы вполне возможно, не хотите внести изменения в существующий код.

Если вы хотите вспомогательный метод для проверки, как это, но рад за это бросить NullPointerException, я бы рекомендовал использовать гуавы и его Preconditions класса, у которого есть это и много других полезных методов проверки.

Также обратите внимание, что Java 1.7 представляет java.util.Objects, который имеет requireNonNull, поэтому вам даже не нужна библиотека сторонних разработчиков.

+1

Является ли 'NullPointerException' правильным способом? Я подумал, что раньше было выбрано «IllegalArgumentException», которое было раньше. – kalsowerus

+0

@kalsowerus 'java.util.Objects.requireNonNull' бросает' NullPointerException', так что да, это нормально. Проверьте мой ответ, увидев несколько примеров (после того, как я его отредактировал). – Dici

+0

@kalsowerus NPE означает, что вы использовали значение «null», когда вы должны использовать ссылку на объект. –

3

Один трюк с текстовой книгой переводит инициализацию из конструктора в метод.Затем, вы можете иметь любой код, который вы хотите перед ним:

public class Rectangle { 

    int width, height; 

    public Rectangle(int width, int height) { 
     init(width, height); 
    } 

    public Rectangle(Rectangle source) { 
     if (source == null) { 
      throw new IllegalArgumentException("source can't be null!"); 
     } 
     init(source.width, source.height); 
    } 

    private void init(int width, int height) { 
     this.width = width; 
     this.height = height; 
    } 
} 
+5

Обратите внимание, что это означает, что 'width' и' height' не могут быть окончательными, что часто является очень значительным недостатком. –

+0

Кроме того, 'private final' избыточен, так как' private' методы не видны из подклассов – Dici

+0

@ Dici yup, это правда, удалил 'final'. – Mureinik

1

Вы можете сделать как этот

int width, height; 

public Rectangle(int width, int height) { 
    this.width = width; 
    this.height = height; 
} 

public Rectangle(Rectangle source) { 
    if(source != null) { 
     width = source.width; 
     height = source.height; 
    } 
} 
+1

Он хотел избежать дублирования кода, вызвав его в базовый конструктор и выбросив исключение проверки. Этот код не отвечает ни одному из этих двух требований. – Dici

+0

Ах, спасибо, большое спасибо. Извините, но это мой первый ответ. –

+0

Не беспокойтесь :). Хорошее путешествие по переполнению стека – Dici

2

Если вы действительно хотите бросить IllegalArgumentException, я думаю, что чистое решение заключается в использовании статический метод вместо конструктора:

public static Rectangle from(Rectangle source) { 
    if (source == null) { 
     throw new IllegalArgumentException("source can't be null!"); 
    } 
    return new Rectangle(source.width, source.height); 
} 

Или вы можете просто добавить метод копирования:

public Rectangle copy() { 
    return new Rectangle(this.width, this.height); 
} 

Я бы предпочел последнее, так как он устраняет необходимость беспокоиться о том, что Rectangle может быть нулевым. Обратите внимание, что это вызовет NPE, если вы используете его с нулевым объектом, что, возможно, является еще одним показателем того, что NPE в порядке.

+0

Обратите внимание, что метод 'copy' может быть проблематичным в случае наследования - все подклассы имеют * *, чтобы переопределить это, чтобы избежать неожиданного поведения. – Hulk