CodeGym /Cursos /C# SELF /Comparando interfaces e classes abstratas

Comparando interfaces e classes abstratas

C# SELF
Nível 24, Lição 2
Disponível

1. Rapidinho sobre as diferenças clássicas

Se alguém falar: "Interface é só um conjunto de assinaturas", pergunta: "Em qual versão do C# você tá codando?" A partir do C# 8 e mais recentes, as interfaces ficaram muito mais poderosas. Bora comparar elas com classes abstratas — não só pelas propriedades clássicas, mas também levando em conta todas as novidades da plataforma .NET.

Se a gente voltar alguns anos — lá no C# 7 — era tudo simples. Classe abstrata pode definir campos e métodos parcialmente implementados, interface — só assinaturas (de métodos, propriedades, eventos, indexadores).
Herança de classe abstrata rola pelo relacionamento "é um" (Is-a), interfaces implementam herança múltipla de comportamento ("sabe fazer", can-do).

Característica Classe abstrata Interface (até C# 8)
Relacionamento is-a can-do
Herança Só uma Múltipla
Campos Pode ter Não pode
Implementação de métodos Pode Não pode
Construtores Pode Não pode
Modificadores de acesso Diversos (public, protected, ...) Só implicitamente public

Como dá pra ver, antes as classes abstratas eram tipo "irmão mais velho" das interfaces — mais poderosas e flexíveis. Mas tudo muda!

2. Interfaces com implementação padrão

Com o lançamento do C# 8 (e ainda mais no C# 14 e .NET 9) as interfaces ganharam um superpoder novo — métodos com implementação padrão, conhecidos como "Default Interface Methods" (DIM).

Como isso fica no código?


public interface IAnimal
{
    void SayHello();

    // Método com implementação padrão!
    void Walk()
    {
        Console.WriteLine("Eu estou andando...");
    }
}

Uau! Agora a interface pode ter implementação de método. E não só um, mas quantos você quiser. Mas tem um detalhe: esses métodos precisam ser declarados com corpo, e o resto (campos, métodos privados, construtores) — ainda não pode.

3. O que as interfaces modernas conseguem

Novidades que todo dev .NET de hoje precisa conhecer:

  • Métodos com implementação padrão.
  • Métodos privados dentro da interface (só pra ajudar outros métodos da mesma interface).
  • Métodos estáticos (desde o C# 8).
  • Propriedades com implementação padrão.
  • Campos estáticos (desde o C# 14 — "static interface members").
  • Membros estáticos abstratos ("abstract static members" — sim, agora a interface pode exigir certos métodos estáticos na implementação!).

Exemplo de interface moderna completa:


public interface ILogger
{
    static int LoggerCount { get; set; } // C# 14

    void Log(string message); // Assinatura (contrato)

    // Implementação padrão
    void LogWarning(string warning)
    {
        Log("[WARNING]: " + warning);
    }
    
    // Método privado auxiliar na interface (C# 8+)
    private void FormatAndLog(string level, string msg)
    {
        Log($"{level}: {msg}");
    }

    // Método estático na interface (C# 8+)
    static void PrintLoggerInfo()
    {
        Console.WriteLine("Interface ILogger — seu melhor parceiro!");
    }
}

Imagina só — antes isso era impossível, tipo deixar um gato trabalhar como segurança do servidor.

4. Classes abstratas: o que mudou?

Classes abstratas... como dizer... não evoluíram muito na última década. Elas ainda podem ter:

  • Campos (inclusive privados, protegidos e estáticos).
  • Métodos implementados e abstratos.
  • Construtores (sim, dá pra criar classes abstratas com lógica de inicialização).
  • Propriedades, eventos, indexadores.
  • Membros estáticos e de instância.

Exemplo de classe abstrata:


public abstract class Animal
{
    public string Name { get; set; }

    public abstract void Speak();

    public virtual void Walk()
    {
        Console.WriteLine($"{Name} está andando com as patinhas!");
    }

    protected void Eat()
    {
        Console.WriteLine($"{Name} está comendo ração.");
    }
}

Classe abstrata ainda é um ótimo lugar pra guardar lógica comum, estado e comportamento pra uma hierarquia de classes.

5. Comparação moderna: tabela com as novidades

Característica Classe abstrata Interface (C# 14+, .NET 9)
Relacionamento is-a (é um) can-do (sabe fazer)
Herança Só uma Múltipla
Campos Sim, qualquer um Só estáticos* (C# 14+)
Construtores Sim Não
Implementação de métodos Sim (virtual/abstract) Sim (default, static, abstract static)
Propriedades com implementação Sim Sim (implementação padrão)
Membros privados Sim Sim (só métodos, C# 8+)
Membros estáticos Sim Sim (C# 8+, com limitações)
Campos estáticos Sim Sim * (C# 14+)
Modificadores de acesso Qualquer um Padrão public ou private

* — Em interfaces, campos estáticos são usados só em casos especiais, e é uma novidade bem recente da linguagem.

6. Quando usar interface e quando classe abstrata: dicas modernas

Interfaces (agora até com implementação padrão) — são pra criar contratos entre componentes. O lance delas: multiplicidade. Sua classe pode implementar dez interfaces diferentes, virando um verdadeiro canivete suíço.

Classe abstrata ainda é a escolha se:

  • Precisa de estado comum (campos), lógica e comportamento que serão herdados por outras classes.
  • Quer lógica padrão mas que pode ser sobrescrita (usa virtual).
  • Quer centralizar inicialização via construtor.

Na vida real, rola muito esse esquema: "contratos puros" ficam nas interfaces, e se precisa de código ou infraestrutura comum pros filhos — cria uma classe base abstrata.


.                     ┌────────────────────────┐
                      │      Interface         │
                      │  (contrato: o que faz) │
                      └─────────┬──────────────┘
                                │
                 ┌──────────────┼──────────────┐
                 │              │              │
           Implementação 1   Implementação 2 ...  Implementação N
             MyLogger     CloudLogger     FileLogger
    
        (pode combinar com herança de classe abstrata)
Esquema: onde usar o quê

7. Cenários — quando cada um ganha

Implementação múltipla:
Digamos que você tem a interface IDrivable e a classe abstrata Vehicle. Agora a classe Car pode herdar de Vehicle e ao mesmo tempo implementar várias interfaces (IDrivable, IRepairable, IInsurable). Se você tivesse uma classe abstrata Repairable, teria que escolher — Vehicle ou Repairable! Interfaces ganham fácil aqui.

Lógica e estado comum:
Digamos que todo "veículo" tem um campo "placa". Isso tem que ser campo de classe abstrata. Em interface não rola campo (pelo menos, fora os estáticos).

Evolução de API:
Uma das revoluções com Default Interface Methods — agora dá pra evoluir interfaces sem quebrar quem já usa.
Por exemplo, adicionou um método novo com implementação padrão na interface — tudo funciona, todas as implementações antigas continuam de boa! Antes isso era dor (ou, se esquecer da dor, era impossível mesmo).

8. Exemplos na prática

No nosso app de estudos, tá pintando logging. Bora criar uma interface ILogger com implementação padrão:


public interface ILogger
{
    void Log(string message);

    // Implementação padrão disponível pra todo mundo que implementa a interface!
    void LogInfo(string info)
    {
        Log("[INFO] " + info);
    }

    // Método estático da interface
    static void PrintHelp()
    {
        Console.WriteLine("Use ILogger pra logar eventos");
    }
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

// Em algum lugar do código:
ILogger logger = new ConsoleLogger();
logger.LogInfo("Sistema iniciado!"); // funciona por causa da implementação padrão

// Chamando método estático da interface
ILogger.PrintHelp();

Se a gente adicionasse um método novo com implementação padrão na interface, todas as implementações existentes (tipo ConsoleLogger) iam ganhar esse método novo automaticamente — sem pânico e sem quebrar nada.

9. Erros e pegadinhas: prática e armadilhas

Nem tudo são flores, viu. Por exemplo, se sua interface tem implementação padrão, mas você acessa o objeto pelo tipo da classe, não pelo tipo da interface, a implementação padrão só tá disponível via interface.


ConsoleLogger log = new ConsoleLogger();
log.LogInfo("Hello"); // Não compila: LogInfo não tá definido na classe!

ILogger log2 = log;
log2.LogInfo("Hello"); // Suave!

Isso parece com implementação explícita de interface. Às vezes é bom pra esconder API "extra", às vezes pega iniciante de surpresa.

2
Tarefa
C# SELF, nível 24, lição 2
Bloqueado
Implementação de interface usando métodos com implementação padrão
Implementação de interface usando métodos com implementação padrão
2
Tarefa
C# SELF, nível 24, lição 2
Bloqueado
Uso de método estático em interface
Uso de método estático em interface
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION