Erro método com BigDecimal

Fala galera. Recentemente eu fiz um teste para uma empresa, uma vaga Java Backend e não fui aprovado. Alguns erros e outras coisas. Mas fiquei na dúvida com o que me foi respondido pelo examinador e gostaria de quem pudesse ajudar. Segue os códigos e depois minha dúvida:
CarrinhoCompras.java

package br.com.improving.carrinho;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class CarrinhoCompras {
	
	private List<Item> listaDeItens; 

  
    public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade) {

    	int posicaoDoItem = -1;
    	
    	for(int i = 0; i < getItens().size() & posicaoDoItem < 0; i++) {
    		
    		Item temp = listaDeItens.get(i);
    		
    		if(temp.getProduto().equals(produto)){
    			
    			posicaoDoItem = i;
    			
    		}    		
    	}
    	
    	try {	    	
    	    if(!(posicaoDoItem < 0)) {
    	    	
    	    	Item temp = listaDeItens.get(posicaoDoItem);
    	    	
    	    	quantidade = temp.getQuantidade() + quantidade;
    	    	
    	    	if (temp.getValorUnitario() == valorUnitario) {
    	    		valorUnitario = temp.getValorUnitario();
    	    	}
    	    	
    	    	//cria novo item e adiciona na lista
    		   Item item = new Item(produto, valorUnitario, quantidade);
    		   
    		   item.setValor(item.getValorTotal());
    		   
    		   listaDeItens.set(posicaoDoItem, temp);
    		   
    	    } else {
    	    	
    	      Item item = new Item(produto, valorUnitario, quantidade);
			 
    	      item.setValor(item.getValorTotal());
    	      
    	      getItens().add(item);
			 }
    	    
    	}   catch(RuntimeException erro) {
    		
    		  erro.getStackTrace();
    		  
    	    }  
    	
    	
    }

    public boolean removerItem(Produto produto) {
    	
    	int posicaoDoItem = -1;
    	
    	//roda pelos itens da lista atras do mesmo
    	for(int i = 0; i < getItens().size() && posicaoDoItem < 0; i++) {
    		
    		Item temp = listaDeItens.get(i);
    		
    		//se encontrar no produto, seta o valor para o item da lista
    		if(temp.getProduto().equals(produto)) {
    			
    			posicaoDoItem = i;
    			
    		}
    	}
    	
    	// se o item estiver na lista, remove pela posicao adicionada anteriormente
    	if(posicaoDoItem > -1) {
    		
    		getItens().remove(posicaoDoItem);
    		
    		return true;
    				
    	} else {
    		
    		return false;
    	}

    }

    public boolean removerItem(int posicaoItem) {
    	try {
    		listaDeItens.remove(posicaoItem);
    		
    		return true;
    		
    	} catch(RuntimeException erro) {
    		
    		return false;
    		
    	}

    }

    public BigDecimal getValorTotal() {
    	
    	//metodos lambda e stream para iterar dentro da lista e somar o valor de todos os itens
    	getItens().stream().forEach(valor -> valor.getValorTotal().plus());
    	
    	return (BigDecimal) getItens();

    }

    public Collection<Item> getItens() {
    	
    	// se lista vazia, cria uma nova
    	if(listaDeItens == null) {
    		
    		listaDeItens = new ArrayList<>();
    	}
    	
    	return listaDeItens;
    	
    }
}

CarrinhoComprasFactory.java

package br.com.improving.carrinho;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CarrinhoComprasFactory {

	private Map<String, CarrinhoCompras> carrinhosDeCompras = new HashMap<>();
	
	public CarrinhoComprasFactory() {
	}


    public CarrinhoCompras criar(String identificacaoCliente) {
    	
    	CarrinhoCompras carrinhoCompras = new CarrinhoCompras();
    	
    	if(!(carrinhosDeCompras.containsKey(identificacaoCliente))) {
    		
    		carrinhosDeCompras.put(identificacaoCliente, carrinhoCompras);

    		
    	} else {
    		
    		carrinhoCompras = null;    		
    		
    	}
    	
    	return carrinhoCompras;

    }

    public BigDecimal getValorTicketMedio() {

    	List<CarrinhoCompras> carrinhoComprasMedio = new ArrayList<>(this.carrinhosDeCompras.values());
    	    	
        BigDecimal valorTicketMedio = (BigDecimal) valorTicket(carrinhoComprasMedio);
        
        return valorTicketMedio.setScale(2, RoundingMode.HALF_EVEN);
        
    }


    public boolean invalidar(String identificacaoCliente) {
    	
    	try {
    		
    		carrinhosDeCompras.remove(identificacaoCliente);
    		
    		return true;
    		
    	    }
    	
    	    catch(RuntimeException erro) {
    	    	
    		 return false;
    	}

    }
    
    private Object valorTicket(List<CarrinhoCompras> carrinhos) {
        carrinhos.stream().forEach(s -> s.getValorTotal().plus().divide(new BigDecimal(carrinhos.size())));
        return carrinhos;
    }
    
    @Override
    public String toString() {
    	return "Cliente: " + carrinhosDeCompras;
    }
    
}

Item.java

package br.com.improving.carrinho;

import java.math.BigDecimal;


public class Item {

    private Produto produto;
    private BigDecimal valorUnitario;
    private int quantidade;
    private BigDecimal valor;


    public Item(Produto produto, BigDecimal valorUnitario, int quantidade) {
    	
    	this.produto = produto;
    	this.valorUnitario = valorUnitario;
    	this.quantidade = quantidade;
    	
    }

    public Produto getProduto() {
    	
    	return this.produto;
    }
    
    public void setValor(BigDecimal valor) {
	
    	this.valor = valor;
	}

    public BigDecimal getValorUnitario() {
    	
    	return this.valorUnitario;
    }

    public int getQuantidade() {
    	
    	return this.quantidade;
    }

    public BigDecimal getValorTotal() {
    	
    	BigDecimal valorTotalItem;
    	
    	BigDecimal vt = new BigDecimal(quantidade);
    	
    	valorTotalItem = vt.multiply(valorUnitario);
    	
    	return valorTotalItem;

    }
    
    @Override
    public String toString() {
        return "Item [Produto=" + produto + ", Valor Unitario=" + valorUnitario + ", Valor=" + valor + ", Quantidade=" + quantidade + "]\n";
    }
}

Produto.java

package br.com.improving.carrinho;

import java.util.Objects;


public class Produto {

    private Long codigo;
    private String descricao;

 
    public Produto(Long codigo, String descricao) {
    	this.codigo = codigo;
    	this.descricao = descricao;
    }

    public Long getCodigo() {
    	
    	return codigo;
    }

    public String getDescricao() {
    	
    	return descricao;
    }
    
    @Override
    public String toString() {
        return "Produto [descricao=" + descricao + "]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Produto)) return false;
        Produto produto = (Produto) o;
        return getCodigo().equals(produto.getCodigo()) &&
                getDescricao().equals(produto.getDescricao());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCodigo(), getDescricao());
    }
}

TesteCarrinho.java

package br.com.improving.carrinho;

import java.math.BigDecimal;

public class TesteCarrinho {

	public static void main(String[] args) {

		BigDecimal v = new BigDecimal("4.12");
		BigDecimal v1 = new BigDecimal("8.24");
		BigDecimal v2 = new BigDecimal("1.14");
		
		BigDecimal v3 = new BigDecimal("3.12");
		BigDecimal v4 = new BigDecimal("5.24");
		BigDecimal v5 = new BigDecimal("2.50");
		BigDecimal v6 = new BigDecimal("6.51");
		
		Produto p = new Produto(1L, "Leite");
		Produto p1 = new Produto(1L, "Carne");
		Produto p2 = new Produto(1L, "Pão");
		
		Produto p3 = new Produto(1L, "Arroz");
		Produto p4 = new Produto(1L, "Feijão");
		Produto p5 = new Produto(1L, "Água");
		Produto p6 = new Produto(1L, "Salsicha");
			
		CarrinhoCompras c = new CarrinhoCompras();
		CarrinhoCompras c1 = new CarrinhoCompras();
		CarrinhoComprasFactory cf = new CarrinhoComprasFactory();
		CarrinhoComprasFactory cf1 = new CarrinhoComprasFactory();
		
		System.out.println(cf.criar("Ricardo"));
		System.out.println(cf.toString());
		//System.out.println(cf.invalidar("Ricardo"));
		System.out.println(" ");
		//System.out.println(cf.toString());
		System.out.println(cf1.criar("Pâmela"));
		System.out.println(cf1.toString());
		
		c.adicionarItem(p, v, 2);
		c.adicionarItem(p1, v1, 3);
		c.adicionarItem(p1, v1, 3);
		c.adicionarItem(p2, v2, 1);
		
		//carrinho 2
		c1.adicionarItem(p3, v3, 2);//arroz
		c1.adicionarItem(p4, v4, 3);//feijao
		c1.adicionarItem(p4, v4, 3);//feijao indice 2
		c1.adicionarItem(p5, v5, 1);//agua
		c1.adicionarItem(p6, v6, 3);//salsicha
		
		//c.removerItem(p1);
		c1.removerItem(p4);
		//c1.removerItem(3);
		
		
		System.out.println("Quantidade de itens: " + c.getItens().size());
		
		System.out.println("Lista itens no carrinho: \n");
		System.out.println(c.getItens());
		
		//imprimir carrinho 2
		System.out.println("Quantidade de itens: " + c1.getItens().size());
		
		System.out.println("Lista itens no carrinho: \n");
		System.out.println(c1.getItens());
		
		cf.getValorTicketMedio();
		cf1.getValorTicketMedio();
		/*AQUI O ERRO
		 * Exception in thread "main" java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class java.math.BigDecimal (java.util.ArrayList and java.math.BigDecimal are in module java.base of loader 'bootstrap')
	at br.com.improving.carrinho.CarrinhoCompras.getValorTotal(CarrinhoCompras.java:155)
	at br.com.improving.carrinho.CarrinhoComprasFactory.lambda$0(CarrinhoComprasFactory.java:92)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at br.com.improving.carrinho.CarrinhoComprasFactory.valorTicket(CarrinhoComprasFactory.java:92)
	at br.com.improving.carrinho.CarrinhoComprasFactory.getValorTicketMedio(CarrinhoComprasFactory.java:60)
	at br.com.improving.carrinho.TesteCarrinho.main(TesteCarrinho.java:68)
	* 
	*/
	}

}

No erro acima desse TesteCarrinho.java diz que não dá para converter BigDecimal para List. E ja tentei mudar para Double mas também não consegui. É possível fazer isso? Imprimir esse valor vindo dessa lista? Tem mais alguma coisa errada nesse método?
Uma outra: na resposta da empresa me disseram que Lista não é uma estrutura de dados indicada para esse tipo de aplicação. Mas penso que Pilha também não seria (problema para remover um item do meio por exemplo) e talvez árvore binária ou um grafo não seria bom também. Isso procede? Uma lista ser ruim para esse tipo de aplicação? Talvez um HashMap?
Desde já obrigado a todos que puderem responder. Já fui dispensado do processo. Até.

Claro que não vai funcionar. BigDecimal e Double são números, List é um conjunto de dados. São tipos incompatíveis, e no método getValorTotal vc tenta converter um pro outro diretamente:

return (BigDecimal) getItens();

Como getItens() retorna a lista, e ela não é um tipo compatível com BigDecimal, então dá erro.

Na verdade este método está estranho. O que vc esperava fazer com esta linha?

getItens().stream().forEach(valor -> valor.getValorTotal().plus())

Pelo comentário, a ideia era somar o valor total dos itens, então assim não vai funcionar também. Pra que complicar? Faça o simpes:

public BigDecimal getValorTotal() {
    BigDecimal total = BigDecimal.ZERO;
    for (Item item: getItens()) {
        total = total.add(item.getValorTotal());
    }
    return total;
}

Se quiser muito usar stream, pode fazer:

public BigDecimal getValorTotal() {
    return getItens().stream().map(Item::getValorTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
}

Pode ser que tenha erros similares em outros lugares (por exemplo, valorTicket retorna carrinhos, que é uma lista, mas o método deveria retornar BigDecimal, então vai dar o mesmo erro, etc). O resto não sei avaliar, porque não tem o enunciado do teste…

O enunciado da função public BigDecimal getValorTicketMedio() é o que segue:

/**
     * Retorna o valor do ticket médio no momento da chamada ao método.
     * O valor do ticket médio é a soma do valor total de todos os carrinhos de compra dividido
     * pela quantidade de carrinhos de compra.
     * O valor retornado deverá ser arredondado com duas casas decimais, seguindo a regra:
     * 0-4 deve ser arredondado para baixo e 5-9 deve ser arredondado para cima.
     *
     * @return BigDecimal
     */

aqui getItens().stream().forEach(valor -> valor.getValorTotal().plus()) é pra somar tudo que tem na lista, e esse valor vai para a função do getTicketMedio() para retornar o BIgDecimal.
Acredito que com stream e lambda facilita muito a vida. Mas obrigado, vou tentar o que vc passou. Abraços.

O erro esta nessa função (public BigDecimal getValorTicketMedio()) e aí a pergunta:

  • Como então retornar o valor total dos itens da lista em BigDecimal sem dar o erro, já que o cast não funciona?
    Obrigado.

Cara ([hugokotsubo]), queria compartilhar uma ideia para esse metodo do ticketMedio… e ver se vc pode me ajudar.
Seria assim:

  • a função valorTicket recebe uma lista de carrinhos do tipo CarrinhoCompras
  • dai soma o valor de todos os itens de carrinho por carrinho e guarda numa variavel VALORTOTAL
  • depois divide pela quantidade de carrinhos e retorna o valor em BigDecimal
  • por fim passa esse valor para a função ticketMedio e retorna o valor já com o ajuste das casas decimais.

Só estou estudando isso para ver se é possível por em prática… se puder me ajudar…agradeço. Obrigado mais uma vez

getItens retorna um Collection<Item>, um Collection jamais será um BigDecimal.

Isso aqui não está somando nada. Veja na documentação do método plus que ele não faz o que vc está pensando. O que ele faz é: para um BigDecimal cujo valor numérico é n, ele só retorna o equivalente a +n (ou seja, na prática ele retorna o mesmo valor). Ele não soma nada.

Como eu já disse, se quisesse somar usando stream, seria algo como:

public BigDecimal getValorTotal() {
    return getItens().stream().map(Item::getValorTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
}

Mas eu ainda prefiro o for simples que eu sugeri acima. Streams são legais, mas se não souber o que está fazendo, não adianta nada, pode ficar até pior. Tente fazer o simples funcionar primeiro (eu acho o for mais simples sim, e se for o caso, mudar pra stream depois fica mais fácil).


Nesse caso, bastaria fazer:

public BigDecimal getValorTicketMedio() {
    BigDecimal total = BigDecimal.ZERO;
    for (CarrinhoCompras carrinho : this.carrinhosDeCompras.values()) {
        total = total.add(carrinho.getValorTotal());
    }
    return total.divide(BigDecimal.valueOf(this.carrinhosDeCompras.size()), 2, RoundingMode.HALF_UP);
}

Veja que o método divide já recebe o scale (coloquei 2, para duas casas decimais) e o modo de arredondamento.


Claro, também daria pra fazer com stream:

public BigDecimal getValorTicketMedio() {
    BigDecimal total = this.carrinhosDeCompras.values().stream().map(CarrinhoCompras::getValorTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
    return total.divide(BigDecimal.valueOf(this.carrinhosDeCompras.size()), 2, RoundingMode.HALF_UP);
}

Mas não sei se ficou realmente “melhor”. Vai da opinião de cada um…

Cara com o for simples funcionou. Mas no TesteCarrinhos.java eu adiciono 2 carrinhos mas e quando chama o método só retorna 0. Mas parou de aparecer o erro. Acho que preciso “criar” uma quantidade de carrinhos, e adicioná-los num Map<>() talvez e depois aplicar o metodo do ticketMedio. Mas obrigado por ajudar.

Teria como resolver isso? Pegar os valores do getItens() e somá-los e retornar o BigDecimal? Obrigado pela ajuda!

Pelo que entendi, CarrinhoComprasFactory tem um Map com vários carrinhos. Sendo assim, não faz sentido ter mais de um factory, pois apenas um já teria todos os carrinhos.

Vc tem que entender a lógica que vc mesmo criou, entendi por cima que seria algo como:

  • criar um CarrinhoComprasFactory
  • o factory cria os carrinhos, usando o método criar
  • cada um desses carrinhos tem seus produtos

Ou seja:

// cria a factory
CarrinhoComprasFactory cf = new CarrinhoComprasFactory();
// cria os carrinhos
CarrinhoCompras c = cf.criar("Ricardo");
CarrinhoCompras c1 = cf.criar("Pâmela");

// adiciona os itens nos carrinhos
c.adicionarItem(p, v, 2);
c.adicionarItem(p1, v1, 3);
c.adicionarItem(p1, v1, 3);
c.adicionarItem(p2, v2, 1);

c1.adicionarItem(p3, v3, 2);//arroz
c1.adicionarItem(p4, v4, 3);//feijao
c1.adicionarItem(p4, v4, 3);//feijao indice 2
c1.adicionarItem(p5, v5, 1);//agua
c1.adicionarItem(p6, v6, 3);//salsicha

System.out.println("Quantidade de itens: " + c.getItens().size());

System.out.println("Lista itens no carrinho: \n");
System.out.println(c.getItens());

//imprimir carrinho 2
System.out.println("Quantidade de itens: " + c1.getItens().size());

System.out.println("Lista itens no carrinho: \n");
System.out.println(c1.getItens());

System.out.println(cf.getValorTicketMedio());

Saída:

Quantidade de itens: 3
Lista itens no carrinho: 

[Item [Produto=Produto [descricao=Leite], Valor Unitario=4.12, Valor=8.24, Quantidade=2]
, Item [Produto=Produto [descricao=Carne], Valor Unitario=8.24, Valor=24.72, Quantidade=3]
, Item [Produto=Produto [descricao=Pão], Valor Unitario=1.14, Valor=1.14, Quantidade=1]
]
Quantidade de itens: 4
Lista itens no carrinho: 

[Item [Produto=Produto [descricao=Arroz], Valor Unitario=3.12, Valor=6.24, Quantidade=2]
, Item [Produto=Produto [descricao=Feijão], Valor Unitario=5.24, Valor=15.72, Quantidade=3]
, Item [Produto=Produto [descricao=Água], Valor Unitario=2.50, Valor=2.50, Quantidade=1]
, Item [Produto=Produto [descricao=Salsicha], Valor Unitario=6.51, Valor=19.53, Quantidade=3]
]
39.05

Cara muito obrigado. Pensar MUITO rápido as vezes chega em coisas inimagináveis. Bora tentar outras vagas. Codar! []´s

Bom dia.
Cara… sei que encerrei esse tópico (se for o caso eu inicio um novo) mas… vc poderia me ajudar com duas outras coisas? (se possível!)
Tem essa função chamada “public boolean removerItem(Produto produto)” que é para remover um item da lista do carrinho pelo produto cadastrado, que é assim:

/**
     * Permite a remoção do item que representa este produto do carrinho de compras.
     *
     * @param produto
     * @return Retorna um boolean, tendo o valor true caso o produto exista no carrinho de compras e false
     * caso o produto não exista no carrinho.
     */
    public boolean removerItem(Produto produto) {
    	
    	int posicaoDoItem = -1;
    	
    	//roda pelos itens da lista atras do mesmo
    	for(int i = 0; i < getItens().size() && posicaoDoItem < 0; i++) {
    		
    		Item temp = listaDeItens.get(i);
    		
    		//se encontrar no produto, seta o valor para o item da lista
    		if(temp.getProduto().equals(produto)) {
    			
    			posicaoDoItem = i;
    			
    		}
    	}
    	
    	// se o item estiver na lista, remove pela posicao adicionada anteriormente
    	if(posicaoDoItem > -1) {
    		
    		getItens().remove(posicaoDoItem);
    		
    		return true;
    				
    	} else {
    		
    		return false;
    	}
    }

… e ele não esta removendo.
E também é possível remover pelo índice na lista, com essa função:

/**
     * Permite a remoção do item de acordo com a posição.
     * Essa posição deve ser determinada pela ordem de inclusão do produto na 
     * coleção, em que zero representa o primeiro item.
     *
     * @param posicaoItem
     * @return Retorna um boolean, tendo o valor true caso o produto exista no carrinho de compras e false
     * caso o produto não exista no carrinho.
     */
    public boolean removerItem(int posicaoItem) {
    	try {
    		listaDeItens.remove(posicaoItem);
    		
    		return true;
    		
    	} catch(RuntimeException erro) {
    		
    		return false;
    		
    	}

    }

… que não vai também. Não sei porque, porque se mandar listar o índice ele mostra onde produto tal esta e qual seu índice. Mas também não remove. No Teste eu executo para adicionar o item e depois remover pelo produto, mas ele aparece na lista de itens.

E a outra coisa é nesse método de criação do CarrinhoComprasFactory, " public CarrinhoCompras criar(String identificacaoCliente)". Segue a descrição e o código:

/**
     * Cria e retorna um novo carrinho de compras para o cliente passado como parâmetro.
     *
     * Caso já exista um carrinho de compras para o cliente passado como parâmetro, este carrinho deverá ser retornado.
     *
     * @param identificacaoCliente
     * @return CarrinhoCompras
     */
    public CarrinhoCompras criar(String identificacaoCliente) {
    	
    	CarrinhoCompras carrinhoCompras = new CarrinhoCompras();
    	
    	if(!(carrinhosDeCompras.containsKey(identificacaoCliente))) {
    		
    		carrinhosDeCompras.put(identificacaoCliente, carrinhoCompras);

    		
    	} else {
    		
    		carrinhoCompras = null;    		
    		
    	}
    	
    	return carrinhoCompras;

    }

E para isso eu crio um HashMap antes para verificar se tem já carrinho com essa String de identificação, mas não verifica…

private Map<String, CarrinhoCompras> carrinhosDeCompras = new HashMap<>();

e se eu fizer assim:

CarrinhoComprasFactory cf = new CarrinhoComprasFactory();

		//add carrinhos na factory
		CarrinhoCompras c = cf.criar("Alexandre");
		CarrinhoCompras c1 = cf.criar("Isabela");
		//criando novo carrinho com mesma identificação
		CarrinhoCompras c2 = cf.criar("Alexandre");
		
		if (c.equals(c2)) {
			
			System.out.println("Mesmo nome retorna null");
		} else {
			System.out.println("Erro ao criar carrinho");
		}

Ele retorna só “Erro ao criar carrinho”. Ou seja mesmo com a mesma identificação ele cria, e não deveria (deveria retornar como null). Teria como me ajudar? Estou com esse quebra-cabeça que tá dureza… Se puder agradeço. Se precisar de outro tópico, o abro. E desde já obrigado. Mais uma vez.

Primeiramente, em vez do método getItens ficar toda hora verificando se a lista é nula, é melhor inicializar a lista no construtor:

public class CarrinhoCompras {
    private List<Item> listaDeItens;
    // adicione um construtor na classe
    public CarrinhoCompras() {
        // inicializa a lista
        listaDeItens = new ArrayList<>();
    }
    // assim, o getter não precisa mais verificar se a lista é null
    public Collection<Item> getItens() {
        return listaDeItens;
    }
}

Remover

Não precisa complicar, você pode remover apenas assim:

public boolean removerItem(Produto produto) {
    Iterator<Item> it = listaDeItens.iterator();
    while (it.hasNext()) {
        Produto p = it.next().getProduto();
        if (p.equals(produto)) {
            it.remove();
            return true; // se já removeu, pode retornar (nem precisa olhar o resto)
        }
    }
    // se chegou aqui é porque não entrou no if acima, ou seja, o produto não existe na lista de itens
    return false;
}

public boolean removerItem(int posicaoItem) {
    // garantir que a posição existe
    if (posicaoItem < 0 || posicaoItem >= listaDeItens.size()) {
        return false;
    }
    listaDeItens.remove(posicaoItem);
    return true;
}

Testando:

// cria produtos, etc (igual ao exemplo anterior)
// ...

CarrinhoComprasFactory cf = new CarrinhoComprasFactory();
CarrinhoCompras c = cf.criar("Ricardo");

c.adicionarItem(p, v, 2);
c.adicionarItem(p1, v1, 3);
c.adicionarItem(p2, v2, 1);
System.out.println(c.getItens().size() + " itens no carrinho antes de remover p1: \n");
System.out.println(c.getItens());

System.out.println("--------------------");
if (c.removerItem(1)) {
    System.out.println(c.getItens().size() + " itens no carrinho depois de remover p1: \n");
    System.out.println(c.getItens());
} else {
    System.out.printf("Produto %d (%s) não foi removido porque não estava no carrinho \n", p1.getCodigo(), p1.getDescricao());
}

Saída:

3 itens no carrinho antes de remover p1: 

[Item [Produto=Produto [descricao=Leite], Valor Unitario=4.12, Valor=null, Quantidade=2]
, Item [Produto=Produto [descricao=Carne], Valor Unitario=8.24, Valor=null, Quantidade=3]
, Item [Produto=Produto [descricao=Pão], Valor Unitario=1.14, Valor=null, Quantidade=1]
]
--------------------
2 itens no carrinho depois de remover p1: 

[Item [Produto=Produto [descricao=Leite], Valor Unitario=4.12, Valor=null, Quantidade=2]
, Item [Produto=Produto [descricao=Pão], Valor Unitario=1.14, Valor=null, Quantidade=1]
]

Criar carrinho

Já o método que cria o carrinho, veja a descrição:

Ou seja, não é para retornar null. Se um carrinho já existe, então você retorna o que já existe. E se não existe, cria um novo. Assim:

public CarrinhoCompras criar(String identificacaoCliente) {
    CarrinhoCompras carrinhoCompras;
    if (this.carrinhosDeCompras.containsKey(identificacaoCliente)) {
        // se já existe, pega do Map
        carrinhoCompras = this.carrinhosDeCompras.get(identificacaoCliente);
    } else {
        // se não existe, cria um novo e coloca no Map
        carrinhoCompras = new CarrinhoCompras();
        carrinhosDeCompras.put(identificacaoCliente, carrinhoCompras);
    }
    return carrinhoCompras;
}

Testando (esse código entra no if corretamente):

CarrinhoCompras c = cf.criar("Alexandre");
CarrinhoCompras c1 = cf.criar("Isabela");
// criando novo carrinho com mesma identificação
CarrinhoCompras c2 = cf.criar("Alexandre");
if (c.equals(c2)) {
    System.out.println("iguais"); // entra no if
} else {
    System.out.println("Erro ao criar carrinho");
}

Outras coisas

Tem outras coisas estranhas. Por exemplo:

if (temp.getValorUnitario() == valorUnitario) {
    valorUnitario = temp.getValorUnitario();
}

Se os valores são iguais, você faz com que eles sejam iguais. O que vc tentou fazer aqui? O que esse código faz basicamente é:

if (1 == x) {
    x = 1;
}

Se x é igual a 1, então x passa a ser 1. Troque x por valorUnitario e 1 por temp.getValorUnitario e veja que não faz sentido.

E em vez de if (!(posicaoDoItem < 0)), por que não fazer if (posicaoDoItem >= 0)?

Enfim, pelo que entendi, o método adicionarItem tem que verificar se um item já existe. Caso exista, adiciona a quantidade e atualiza o valor unitário. Se não existir, adiciona. Sendo assim, dá para simplificar. Na classe Item adicione um método para atulizar a quantidade e um setter para o valor unitário:

public class Item {
    // adicione este método
    public void adicionarQuantidade(int qtd) {
        this.quantidade += qtd;
    }
    public void setValorUnitario(BigDecimal valorUnitario) {
        this.valorUnitario = valorUnitario;
    }
}

E no CarrinhoCompras mude para:

public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade) {
    boolean existe = false;
    for (Item item : listaDeItens) {
        if (item.getProduto().equals(produto)) {
            existe = true;
            item.setValorUnitario(valorUnitario);
            item.adicionarQuantidade(quantidade);
            break; // se já encontrei, não tem porque continuar o for, então posso interrompê-lo
        }
    }
    // se não existe, tem que adicionar
    if (!existe) {
        listaDeItens.add(new Item(produto, valorUnitario, quantidade));
    }
}

Então no teste eu fiz:

c.adicionarItem(p1, v1, 3);
c.adicionarItem(p1, v1, 3);

E no final a quantidade do produto é 6 (porque eu adicionei 3 e depois mais 3).


Outra coisa, por que Item tem valorUnitario e valor? O valor seria o que? Eu até achei que seria o valor total, mas como já tem o método getValorTotal, então não sei para que serve esse valor.

Esse “valor” era uma tentativa de artificio técnico para resolver a questão do valor unitário. Ficou e e esqueci de tirar na criação do post. Erro de conferência da minha parte.

Olha… muito obrigado por revisar todo meu código. Eu nunca trabalhei com carteira assinada, ou mesmo com contrato em uma software house ou empresa grande… depois das faculdades e pós e cursos estou tentando sair dessa vida de funcionário público para tentar vaga como estagiário ou júnior ou sei-lá-o-que dentro de programação… mas sem o feeling é meio difícil… Pensar e colocar em código é complicado. Mais uma vez obrigado. Sei que devo ter torrado sua paciência. Até mais!

… então… a ideia dessa parte vem daqui:

/**
     * Permite a adição de um novo item no carrinho de compras.
     *
     * Caso o item já exista no carrinho para este mesmo produto, as seguintes regras deverão ser seguidas:
     * - A quantidade do item deverá ser a soma da quantidade atual com a quantidade passada como parâmetro.
     * - Se o valor unitário informado for diferente do valor unitário atual do item, o novo valor unitário do item deverá ser
     * o passado como parâmetro.
     *
     * Devem ser lançadas subclasses de RuntimeException caso não seja possível adicionar o item ao carrinho de compras.
     *
     * @param produto
     * @param valorUnitario
     * @param quantidade
     */

da função public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade)

A princípio de acordo com o que precisava na descrição, estava funcionando… mas agora vou por na ponto do lápis para ver. Obrigado, de novo : ) !!!

Então o que eu fiz acima está certo. Talvez só precise ajustar o detalhe do valor unitário mudar apenas se ele for diferente do item:

public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade) {
    boolean existe = false;
    for (Item item : listaDeItens) {
        if (item.getProduto().equals(produto)) {
            existe = true;
            if (item.getValorUnitario() != valorUnitario) { // se o valor mudou, altera o item
                item.setValorUnitario(valorUnitario);
            }
            item.adicionarQuantidade(quantidade);
            break; // se já encontrei, não tem porque continuar o for, então posso interrompê-lo
        }
    }
    // se não existe, tem que adicionar
    if (!existe) {
        listaDeItens.add(new Item(produto, valorUnitario, quantidade));
    }
}

uma dúvida… quando ele pede assim:

* Devem ser lançadas subclasses de RuntimeException caso não seja possível adicionar o item ao carrinho de compras.

na função public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade)
precisa de um try-catch para conseguir usar as tais subclasses de RuntimeException, certo?

Não, se é para LANÇAR a exceção então você vai por um throw

Bom, o método que eu fiz acima está tão simples que não sei qual tipo de exceção poderia ser lançada (talvez NullPointerException em alguma situação específica).

Enfim, talvez seja interessante incluir alguma validação. Por exemplo, se a quantidade for negativa, não faz sentido adicionar o item, então poderia ser algo assim:

public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade) {
    if (produto == null) {
        throw new IllegalArgumentException("Produto não pode ser nulo");
    }
    if (valorUnitario == null || valorUnitario.compareTo(BigDecimal.ZERO) <= 0) {
        throw new IllegalArgumentException("Valor unitário deve ser maior que zero");
    }
    if (quantidade <= 0) {
        throw new IllegalArgumentException("Quantidade deve ser maior que zero");
    }

    boolean existe = false;
    for (Item item : listaDeItens) {
        if (item.getProduto().equals(produto)) {
            existe = true;
            item.setValor(valorUnitario);
            item.adicionarQuantidade(quantidade);
            break; // se já encontrei, não tem porque continuar o for, então posso interrompê-lo
        }
    }
    // se não existe, tem que adicionar
    if (!existe) {
        listaDeItens.add(new Item(produto, valorUnitario, quantidade));
    }
}

Ou seja, se algum dado for inválido, ele não adiciona o item (inventei as regras da minha cabeça, tem que ver se tem algum outro requisito dizendo o que pode ou não ser adicionado).

Quanto ao try/catch, vc usa na hora de adicionar algo:

CarrinhoCompras c = // cria o carrinho

try {
    c.adicionarItem(produto, valor, quantidade);
} catch (IllegalArgumentException e) {
    System.out.println("Erro ao adicionar: " + e.getMessage());
}