java 中的异常或空值

Exceptions or null in java

我有下疑问。根据java的良好实践,如何管理找不到对象的情况,我们想知道为什么。

例如,如果有人在登录我们的系统时遇到问题,我们想准确地告知他们问题出在哪里,我们不能 return null 因为我们失去了无法登录的原因。对于示例:

public User login(String username, String password) {
    boolean usernameEmpty = (credentials.getUsername()==null || credentials.getUsername().isEmpty());
    boolean passwordEmpty = (credentials.getPassword()==null || credentials.getPassword().isEmpty());
    //getUserPassword return null if doesn't exist an user with username and password return null
    User user = getUserPassword(username,password);

    if (!usernameEmpty && !passwordEmpty && user!=null) {
        LOGGER.info("Found " + username);
    } else if (!usernameEmpty && !passwordEmpty && user==null) {
        LOGGER.info("There is no such username and password: " + username);
    } else if (usernameEmpty) {
        LOGGER.info("Username can not be empty ");
    } else if (passwordEmpty) {
        LOGGER.info("Password can not be empty ");
    }

    return user;
}

我能想到两种各有利弊的方案来解决。

第一个 一个在于使用异常,但我认为使用与预期不同的场景(如异常)不是一个好主意。因此,我将其丢弃。

第二一个是将对象(用户)包含在另一个对象中以管理不同的可能性。例如,使用这样的东西:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private String exceptionMessage;

    //getters / setters / ..
}

public EntityObject<User> login(String username, String password) {
    boolean usernameEmpty = (credentials.getUsername()==null || credentials.getUsername().isEmpty());
    boolean passwordEmpty = (credentials.getPassword()==null || credentials.getPassword().isEmpty());
    User user = getUserPassword(username,password);
    EntityObject<User> entity = null;

    if (!usernameEmpty && !passwordEmpty && user!=null) {
        LOGGER.info("Found " + username);
        entity = new EntityObject<User>(user);
    } else if (!usernameEmpty && !passwordEmpty && user==null) {
        entity = new EntityObject<User>("There is no such username and password: " + username); 
    } else if (usernameEmpty) {
        entity = new EntityObject<User>("Username can not be empty ");
    } else if (passwordEmpty) {
        entity = new EntityObject<User>("Password can not be empty ");
    }

    return entity;
}

与第一个选项相比,我更喜欢第二个选项,但我不喜欢我必须将方法签名更改为 return 不同于通常的 class (EntityObject) (User ).

通常是什么?通常如何管理? 非常感谢

当系统中发生异常时,应该使用异常。对于正常流程和预期会发生的事情,您应该避免使用异常。

遵循良好的 SOLID 原则,您的方法应该只做一件事。因此,如果这是一种通过用户名和密码查找用户的方法,我会说最好的方法是 return null(如果使用可选,则为空可选)。原因不丢失。实际上很清楚 - 没有使用提供的用户名和密码找到这样的用户(这个原因包括用户名为空的问题,并且该方法的用户向登录方法提供空用户名是错误的)。将复杂的逻辑添加到方法中并为此类事情添加额外的实体将使您的代码更难维护和理解。这个方法的工作不是处理验证。

如果 class 被网站或其某种 API 使用,那么他们可以处理验证(如果用户名或密码为空)。

对我来说,第二个选项看起来更好。可能,要知道错误是什么而不是在 java 代码中编写消息,你可以创建 enum 可能的场景并在前端代码中解决它,如果你真的需要消息,你可以在枚举中创建构造函数来存储它。它将简化支持并在将来使用对象。另外,添加更多场景不会对您造成太大伤害。

基本版:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private enum auth {
        NO_PASSWORD, NO_USERNAME, USER_DOES_NOT_EXIST, SUCCESS    
    }
}

带有 enum 构造函数的版本:

public class EntityObject<t> {
    //Is used to return the entity or entities if everything was fine
    private t entity;
    //Is used to inform of any checked exception
    private enum auth {
        NO_PASSWORD("Password cannot be empty"),
        NO_USERNAME("Username cannot be empty"), 
        USER_OR_PASSWORD_DOES_NOT_EXIST("No such username or password exist"),
        SUCCESS("OK");
        public String message;
        public auth(String message) {
            this.message = message;
        }   
    }
}

我会说第二种方法非常好。如果我是你,我会那样做。

如果您真的不想更改return值,您可以添加另一种检查用户是否可以登录的方法:

public static final String SUCCESS = "Success"
public String checkLoginError(String username, String password) {
    // do all the checks and return the error message
    // return SUCCESS if no error
}

现在login方法可以是一行:

return getUserPassword(username,password);

你可以这样使用它:

String loginResult = checkLoginError(...);
if (loginResult.equals(SUCCESS)) {
    User loggedInUser = login(...)
} else {
    // do stuff with the error message stored in loginResult
}

您的问题似乎源于一个方法,该方法负责多个问题。

我认为 login 方法不应该检查这些值是否为空。大概有某种 UI(图形或非图形)正在获取用户名和密码 - 这应该是对用户输入执行验证的层。

login 方法应该只关心给定的凭据是否与您系统中的用户匹配。只有两个结果——是或否。为此,您可以使用 Optional<User>。它应该容忍字符串为空,因为这无论如何都不会匹配用户(大概用户不可能存在于这种状态)。

这是一些伪代码:

void loginButtonPressed()
{
    if (usernameTextBox.text().isEmpty())
    {
        errorPanel.add("Username cannot be blank");
    }
    else if (passwordTextBox.text().isEmpty())
    {
        errorPanel.add("Password cannot be blank");
    }
    else
    {
        login(usernameTextBox.text(), passwordTextBox.text());
        // assign above result to a local variable and do something...
    }
}

public Optional<User> login(String username, String password)
{
    Optional<User> user = Optional.ofNullable(getUserPassword(username, password));
    user.ifPresentOrElse(
        user -> LOGGER.info("Found " + username),
        () -> LOGGER.info("Not found")
    );
    return user;
}

Java 的 null 值是该语言最糟糕的方面之一,因为在它发生之前您无法真正判断方法是否正在接收空值。如果您使用的是 IDE (我希望如此),您可以检查它是否可以控制您是否传递了 null 不应该存在的值(IntelliJ 可以通过添加 @NotNull 方法参数的注解)。

因为它可能很危险,所以最好避免传递 nulls,因为一旦您的代码变得有点复杂,它肯定会导致错误。

此外,我认为检查 null 是合理的,只有当 确实有可能存在时。

如果你想表达一个值可以存在或不存在,最好使用Optional<T>。如果出于某种原因,可以传递 null 值而不是实际值,您可以创建一个实用方法,其唯一关心的是验证参数是否正确:

public Optional<EntityObject<User>> login(String username, String password) {
    //isNotNull shouldn't be necessary unless you can't validate your parameters
    //before passing them to the method.
    //If you can, it's not necessary to return an Optional
    if (isNotNull(username, password)) {
        //Since I don't know if a password must always be present or not 
        //I'm assuming that getUserPassword returns an Optional
        return Optional.of(new EntityObject<User>(getUserPassword(username,password).orElse(AN_EMPTY_USER)));
    } else {
        return Optional.Empty();
    }
}

无论如何,我认为验证输入不应该是 login 方法的问题,即使您不想使用 Optional;它应该用另一种方法来完成。