Programa Java com erro: missing return statement

Obs.: O programa está no início. Sou iniciante em Java, e antes de dar continuidade no programa, quero resolver os erros pertinentes no momento.

Erro:

O programa:
Parte1:

// Exercício 3.17 Computadorização dos registros de saúde

// Para exibir janelas
import javax.swing.JOptionPane;

public class PerfilSaudeTeste
{
	public static void main(String[] args)
	{
		PerfilSaude paciente = new PerfilSaude();
		
		String dia2 = JOptionPane.showInputDialog(null, "Digite o dia de hoje: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int diaAtual = Integer.parseInt(dia2);
		paciente.setDiaAtual(diaAtual);
		
		String mes2 = JOptionPane.showInputDialog(null, "Digite o mês atual: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int mesAtual = Integer.parseInt(mes2);
		paciente.setMesAtual(mesAtual);
		
		String ano2 = JOptionPane.showInputDialog(null, "Digite o ano atual: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int anoAtual = Integer.parseInt(ano2);
		paciente.setAnoAtual(anoAtual);
		
		String nome = JOptionPane.showInputDialog(null, "Digite seu nome: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		paciente.setNome(nome);
		
		String sobrenome = JOptionPane.showInputDialog(null, "Digite seu sobrenome: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		paciente.setSobrenome(sobrenome);
		
		// Array para modificar os botões do JOptionPaneOptionMessage
		Object [] arranjo = {"Masculino", "Feminino"};
		int genero = JOptionPane.showOptionDialog(null, "Seu gênero.", "Perfil de Saúde do Paciente", 0, JOptionPane.PLAIN_MESSAGE, null, arranjo, arranjo[0]);
		paciente.setGenero(genero);
		
		String dia1 = JOptionPane.showInputDialog(null, "Digite o dia em que nasceu: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int dia = Integer.parseInt(dia1);
		paciente.setDiaNasc(dia);
		
		String mes1 = JOptionPane.showInputDialog(null, "Digite o mês em que nasceu: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int mes = Integer.parseInt(mes1);
		paciente.setMesNasc(mes);
		
		String ano1 = JOptionPane.showInputDialog(null, "Digite o ano em que nasceu: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		int ano = Integer.parseInt(ano1);
		paciente.setAnoNasc(ano);
		
		String altura1 = JOptionPane.showInputDialog(null, "Digite a sua altura em metros: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		double altura = Double.parseDouble(altura1);
		paciente.setAltura(altura);
		
		String peso1 = JOptionPane.showInputDialog(null, "Digite o seu peso em quilogramas: ", "Perfil de Saúde do Paciente", JOptionPane.PLAIN_MESSAGE);
		double peso = Double.parseDouble(peso1);
		paciente.setPeso(peso);
		
		
		System.out.printf("%s", paciente.getIdade());
		
	}
}

Parte 2:

public class PerfilSaude
{
	private String nomeVI;
	private String sobrenomeVI;
	private int generoVI;
	private int diaNascVI;
	private int mesNascVI;
	private int anoNascVI;
	private double alturaVI;
	private double pesoVI;
	private int diaAtualVI;
	private int mesAtualVI;
	private int anoAtualVI;
		
	// Métodos set
	public void setDiaAtual(int diaAtualVL)
	{
		diaAtualVI = diaAtualVL;
	}
	
	public void setMesAtual(int mesAtualVL)
	{
		mesAtualVI = mesAtualVL;
	}
	
	public void setAnoAtual(int anoAtualVL)
	{
		anoAtualVI = anoAtualVL;
	}
	
	public void setNome(String nomeVL)
	{
		nomeVI = nomeVL;
	}
	
	public void setSobrenome(String sobrenomeVL)
	{
		sobrenomeVI = sobrenomeVL;
	}
	
	public void setGenero(int generoVL)
	{
		generoVI = generoVL;
	}
	
	public void setDiaNasc(int diaNascVL)
	{
		diaNascVI = diaNascVL;
	}
	
	public void setMesNasc(int mesNascVL)
	{
		mesNascVI = mesNascVL;
	}
	
	public void setAnoNasc(int anoNascVL)
	{
		anoNascVI = anoNascVL;
	}
	
	public void setAltura(double alturaVL)
	{
		alturaVI = alturaVL;
	}
	
	public void setPeso(double pesoVL)
	{
		pesoVI = pesoVL;
	}
	
	// Métodos get
	public String getNome()
	{
		return nomeVI;
	}
	
	public String getSobrenome()
	{
		return sobrenomeVI;
	}
	
	public int getGenero()
	{
		return generoVI;
	}
	
	public int getDiaNasc()
	{
		return diaNascVI;
	}
	
	public int getMesNasc()
	{
		return mesNascVI;
	}
	
	public int getAnoNasc()
	{
		return anoNascVI;
	}
	
	public double getAltura()
	{
		return alturaVI;
	}
	
	public double getPeso()
	{
		return pesoVI;
	}
	
	// Calcula a idade do paciente
	public String getIdade()
	{
		if (anoNascVI == anoAtualVI)
		{
			// O usuário tem 0 anos de idade
			return String.format("0 anos de idade.");
		}
		
		if (anoNascVI > anoAtualVI)
		{
			if (mesNascVI < mesAtualVI)
			{
				// O usuário tem X anos de idade
				return String.format("%d anos de idade.", anoAtualVI - anoNascVI - 1);
			}
			
			if (mesNascVI == mesAtualVI)
			{
				if (diaNascVI < diaAtualVI)
				{
					// O usuário tem X anos de idade
					return String.format("%d ano de idade.", anoAtualVI - anoNascVI - 1);
				}
				
				if (diaNascVI == diaAtualVI)
				{
					// O usuário tem X anos de idade
					return String.format("%d ano de idade.", anoAtualVI - anoNascVI);
				}
			}
		}
	}
}

Seu método public String getIdade() tem várias condições (ifs), e se elas não forem satisfeitas, o método não vai retornar nada, o que não é permitido (ele precisa retornar uma String, mesmo que vazia). Adicione os devidos else nas cláusulas if desse método, ou algum retorno padrão no final do método.

Abraço.

O problema está no método que calcula a idade:

public String getIdade() {
    if (anoNascVI == anoAtualVI) {
        // O usuário tem 0 anos de idade
        return String.format("0 anos de idade.");
    }
    if (anoNascVI > anoAtualVI) {
        if (mesNascVI < mesAtualVI) {
            // O usuário tem X anos de idade
            return String.format("%d anos de idade.", anoAtualVI - anoNascVI - 1);
        }
        if (mesNascVI == mesAtualVI) {
            if (diaNascVI < diaAtualVI) {
                // O usuário tem X anos de idade
                return String.format("%d ano de idade.", anoAtualVI - anoNascVI - 1);
            }
            if (diaNascVI == diaAtualVI) {
                // O usuário tem X anos de idade
                return String.format("%d ano de idade.", anoAtualVI - anoNascVI);
            }
        }
    }
}

O que acontece se anoNascVI for menor que anoAtualVI? Ele não entra em nenhum dos if's e o método não retorna nada. Mas ele precisa retornar algo sempre.

E de qualquer forma, o cálculo está errado. Se a pessoa nasceu em 2000 e o ano atual é 2021, por exemplo, ele não entra em nenhum dos if's (pois o ano de nascimento não é maior e nem igual ao ano atual).

Outro ponto é que a idade é um número, e não faz muito sentido o método já retornar a mensagem formatada. Eu prefiro que o método só retorne o valor da idade, e depois quem chamou o método que mostre o valor da maneira que achar melhor. Enfim, uma forma de calcular seria:

public class PerfilSaude {
    // construtor, getters/setters, etc...

    private int normalize(int mes, int ano) {
        return 12 * ano + mes;
    }

    // Idade é um número
    public int getIdade() {
        int nasc = normalize(mesNascVI, anoNascVI);
        int atual = normalize(mesAtualVI, anoAtualVI);
        int idadeMeses = atual - nasc;
        if (diaNascVI > diaAtualVI) { // ainda não chegou o aniversário
            idadeMeses--;
        }
        return idadeMeses / 12;
    }
}

E como já dito, mudei o método para retornar apenas o valor numérico da idade, e quem chamou o método que faça o que quiser com ele:

PerfilSaude paciente = new PerfilSaude(etc...);

int idade = paciente.getIdade();
System.out.printf("%d ano%s de idade\n", idade, idade == 1 ? "" : "s");

Assim é melhor porque se eu quiser mudar a mensagem, ou se eu quiser mostrar mensagens diferentes em outros lugares (ou se eu só preciso do valor numérico, por exemplo, para verificar se o usuário se encontra em determinada faixa etária, etc), posso usar o mesmo método. Por exemplo:

int idade = paciente.getIdade();

// posso mudar a mensagem sem precisar mudar o método
System.out.println("Idade=" + idade);
// posso usar o valor da idade como quiser
if (idade >= 18) {
    System.out.println("Maior de idade");
}

Da forma que você fez fica mais “travado”, menos flexível e menos reusável.


Outro ponto é que, em vez de setar os campos do PerfilSaude um a um, acho que seria melhor fazer tudo no construtor. Quando você cria um new PerfilSaude(), ele tem todos os campos zerados. Mas faz sentido um perfil ter o dia, mês e ano iguais a zero? Eu prefiro ter um construtor que recebe tudo que precisa, aí você garante que sempre terá uma instância válida. Algo assim:

public class PerfilSaude {
    private boolean datasValidas(int diaNascVI, int mesNascVI, int anoNascVI, int diaAtualVI, int mesAtualVI, int anoAtualVI) {
        if (anoAtualVI < anoNascVI) {
            return false;
        }
        // aqui eu sei que o ano de nascimento é menor ou igual ao ano atual
        if (mesAtualVI < mesNascVI) {
            return false;
        }
        // aqui eu sei que o mês de nascimento é menor ou igual ao mês atual
        if (diaAtualVI < diaNascVI) {
            return false;
        }
        return true;
    }

    public PerfilSaude(String nomeVI, String sobrenomeVI, int generoVI, double alturaVI, double pesoVI,
            int diaNascVI, int mesNascVI, int anoNascVI, int diaAtualVI, int mesAtualVI, int anoAtualVI) {
        if (!datasValidas(diaNascVI, mesNascVI, anoNascVI, diaAtualVI, mesAtualVI, anoAtualVI)) {
            throw new IllegalArgumentException("Data de nascimento não pode ser depois da data atual");
        }
        this.nomeVI = nomeVI;
        this.sobrenomeVI = sobrenomeVI;
        this.generoVI = generoVI;
        this.diaNascVI = diaNascVI;
        this.mesNascVI = mesNascVI;
        this.anoNascVI = anoNascVI;
        this.alturaVI = alturaVI;
        this.pesoVI = pesoVI;
        this.diaAtualVI = diaAtualVI;
        this.mesAtualVI = mesAtualVI;
        this.anoAtualVI = anoAtualVI;
    }
        // getters/setters, etc...
}

Assim você cria o PerfilSaude passando tudo que precisa, e até incluí uma validação para as datas. Dessa forma, você tem certeza que ou vai criar uma instância com todos os dados válidos, ou então o programa lança uma exceção e nem sequer prossegue (pois não faria sentido prosseguir com dados inválidos).

Aí ficaria assim no main:

String nome = // ler o nome...
int diaAtual = // ler o dia...
// ler todos os dados (mas não chame os setters)

// crie o PerfilSaude aqui (o construtor já seta tudo e valida os dados, etc)
PerfilSaude paciente = new PerfilSaude(nome, sobrenome, genero, altura, peso, dia, mes, ano, diaAtual, mesAtual, anoAtual);
// aqui eu tenho certeza que a instância é válida e tem todos os dados necessários
int idade = paciente.getIdade();

Você até pode remover os setters se quiser, não sei se faz sentido ficar mudando os dados do perfil (afinal, se mudou algum dado, então é outro perfil, não? Claro que depende dos requisitos, mas de forma geral, não crie o hábito de criar getters e setters automaticamente pra tudo, crie só o que fizer sentido).


E só para constar, em um código mais “sério” (um sistema em produção), em vez de fazer o cálculo manualmente, prefira usar o que já tem pronto. A partir do Java 8, você poderia usar um java.time.LocalDate para guardar as datas, e calcular a idade com um java.time.temporal.ChronoUnit:

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class PerfilSaude {
    private LocalDate dataNasc;
    private LocalDate dataAtual;
    // demais campos, getters/setters, etc
    
    public PerfilSaude(String nomeVI, String sobrenomeVI, int generoVI, double alturaVI, double pesoVI,
            int diaNascVI, int mesNascVI, int anoNascVI, int diaAtualVI, int mesAtualVI, int anoAtualVI) {
        this.dataNasc = LocalDate.of(anoNascVI, mesNascVI, diaNascVI);
        this.dataAtual = LocalDate.of(anoAtualVI, mesAtualVI, diaAtualVI);
        if (this.dataAtual.isBefore(this.dataNasc)) {
            throw new IllegalArgumentException("Data de nascimento não pode ser depois da data atual");
        }
        this.nomeVI = nomeVI;
        this.sobrenomeVI = sobrenomeVI;
        this.generoVI = generoVI;
        this.alturaVI = alturaVI;
        this.pesoVI = pesoVI;
    }

    public int getIdade() {
        return (int) ChronoUnit.YEARS.between(this.dataNasc, this.dataAtual);
    }
}

Outra vantagem é que LocalDate.of também valida os valores (por exemplo, dá erro para dia maior que 31 ou 29 de fevereiro em ano não-bissexto, etc).

Eu também revisaria os nomes dos campos, não entendi o que é esse sufixo VI (só mantive por preguiça de mudar mesmo).

1 curtida

@hugokotsubo Muito obrigado pela atenção, tem coisas que são novas para mim, vou estudar e aprender com o seu comentário.
Os nomes das variáveis eu gosto de abreviar, por exemplo, o sufixo VI, significa Variável de Instância, e o sufixo VL, significa Variável Local. O livro que estou lendo comenta sobre.
Mas, muito obrigado :grinning::pray:

Eu particularmente não gosto de usar sufixos pra isso. Basta padronizar pra sempre usar this.variavel, por exemplo, que você já sabe que é variável de instância.

Sem contar que muitas IDE’s já colocam cores diferentes pra variáveis de instância, etc, enfim, na minha opinião tem jeitos melhores de resolver isso…

1 curtida

É que o livro só comentou sobre o assunto, sem especificar qual das variáveis era de instância e quais eram locais. Dai comecei a mudar o programa até achar a diferença, assim eu aprendi quem é quem.