2017-01-28 1 views
3

Мне может понадобиться помощь с проблемой, с которой я сталкиваюсь с генериками Java. Я построил небольшой пример, чтобы показать, что я имею в виду.Ошибка доступа к генерическим файлам Java

Handler

package generalizingTest; 

public class Handler<S extends Server<?>> { 
    public S server; 

    public Handler(S server) { 
     this.server = server; 
    } 
} 

SubHandler

package generalizingTest; 

public class SubHandler<S extends Server<?>> extends Handler<S> { 

    public SubHandler(S server) { 
     super(server); 
    } 

    public void subHandlerMethod() { 

    } 

} 

Server

package generalizingTest; 

import java.util.ArrayList; 

public class Server<H extends Handler<?>> { 
    public ArrayList<H> handlers; 

    public Server() { 
     handlers = new ArrayList<H>(); 
    } 

    public void addHandler(H c) { 
     handlers.add(c); 
    } 
} 

SubServer

package generalizingTest; 

public class SubServer<H extends Handler<?>> extends Server<H> { 
    public void subServerMethod() { 

    } 
} 

class diagram

Startup

package generalizingTest; 

public class Startup { 

    public static void main(String[] args) { 
     Server<Handler<?>> serverWHandler = new Server<Handler<?>>(); 
     Server<SubHandler<?>> serverWSubHandler = new Server<SubHandler<?>>(); 

     SubServer<Handler<?>> subServerWHandler = new SubServer<Handler<?>>(); 
     SubServer<SubHandler<?>> subServerWSubHandler = new SubServer<SubHandler<?>>(); 

     Handler<Server<?>> handlerWServer = new Handler<Server<?>>(serverWHandler); 
     Handler<SubServer<?>> handlerWSubServer = new Handler<SubServer<?>>(subServerWHandler); 

     SubHandler<Server<?>> subHandlerWServer = new SubHandler<Server<?>>(serverWSubHandler); 
     SubHandler<SubServer<?>> subHandlerWSubServer = new SubHandler<SubServer<?>>(subServerWSubHandler); 

     serverWHandler.addHandler(handlerWServer); 
     subServerWHandler.addHandler(handlerWSubServer); 

     serverWSubHandler.addHandler(subHandlerWServer); 
     subServerWSubHandler.addHandler(subHandlerWSubServer); 

     subServerWHandler.subServerMethod(); 
     subServerWSubHandler.subServerMethod(); 

     handlerWSubServer.server.subServerMethod(); 
     subHandlerWSubServer.server.subServerMethod(); 

     subHandlerWServer.subHandlerMethod(); 
     subHandlerWSubServer.subHandlerMethod(); 

     System.out.println(subHandlerWSubServer.server.handlers.get(0).getClass().getName()); // SubHandler 

     //produces an error: 
     /* 
     * Unresolved compilation problem: 
     * The method subHandlerMethod() is undefined for the type Handler<capture#9-of ?> 
     */ 
     //subHandlerWSubServer.server.handlers.get(0).subHandlerMethod(); 

    } 
} 

Я только начал изучать дженериков. Они кажутся эффективными, но я не уверен, правильно ли я решил проблему цикла generics() с подстановочным знаком и почему эти ошибки возникают.

Я действительно надеюсь, что кто-то может мне помочь.

EDIT:

Так что, похоже, что я не особо исходной задачи достаточно. Ниже должно быть возможно в любой глубине:

subHandlerWSubServer.server.handlers.get(0).server.handlers.get(0).server.handlers.get(0). ... .server.handlers.get(0).subHandlerMethod(); 

EDIT:

Так эта проблема, кажется, не разрешимы из-за бесконечный цикл определения или отсутствующего self значения, см Siguza’s anwser. Вот discussion между Siguza, user889742 и myself об этой теме.

+0

@MK. Вы удалили там комментарии? – Feirell

+0

Непонятно, чего вы пытаетесь достичь, но я не думаю, что это имеет смысл. Вам действительно нужно иметь два класса, специализирующихся друг на друге? Я не могу представить, почему вам это понадобится. Неудивительно, что попытка выразить это неестественное кровосмесительное отношение будет соответствовать сопротивлению от системы типа языка. –

+0

@ MK. представленный пример нуждается в таких отношениях, когда обеим сторонам нужно знать все методы/переменные-члены противоположности, которые достижимы с помощью генериков. – Feirell

ответ

3

Если я правильно вас понял, a Handler<S> вы хотите, чтобы все обработчики на этом сервере были типа Handler<S>, правильно?
Для этого S.add() должны были только принимать объекты типа Handler<S>. Но для того, чтобы осуществить, что в базовом классе Server, вам нужно будет S, так что:

public class Server<H> 
{ 
    public ArrayList<H<S>> handlers; 

    public Server() 
    { 
     handlers = new ArrayList<H<S>>(); 
    } 

    public void addHandler(H<S> c) 
    { 
     handlers.add(c); 
    } 
} 

Единственная проблема состоит в том, что S не определен, и вы не можете легко определить его. Вам понадобится то, что в контексте Server означает Server, а в контексте SubServer означает SubServer. В основном this.getClass(), но как выражение типа времени компиляции. Если Java имел ключевое слово, что, скажем, self, вы могли бы использовать его как это:

public class Server<H> 
{ 
    public ArrayList<H<self>> handlers; 

    public Server() 
    { 
     handlers = new ArrayList<H<self>>(); 
    } 

    public void addHandler(H<self> c) 
    { 
     handlers.add(c); 
    } 
} 

Тогда Server.add() бы Handler<Server> и SubServer.add() бы Handler<SubServer>.

К сожалению, Java has no such thing, поэтому то, что вы пытаетесь сделать, невозможно.

Java делает много вещей хорошо.
Дженерики не являются одним из них.

+0

Но как я могу это решить? Любая попытка, которую я мог представить, привела бы к бесконечной рекурсии в определении дженериков. Решение 'instance of' похоже не похоже на то, что оно предназначено для использования по умолчанию. – Feirell

+0

'SubHandler > subHandlerWSubServer = new SubHandler > (subServerWSubHandler);' не должен ли это предотвращать именно этот случай, поэтому не может быть суперкласс SubHandler? – Feirell

0

Edit:

Вы можете просто ограничить Субсерверы только принимать SubHandlers

Subserver<H extends SubHandler> extends Server<H> 

Когда вы сделаете это, вы увидите, что ошибка компиляции уходит. Но я понимаю, что вы хотите определить некоторые серверы, которые разрешают только субхандлеры и некоторые поддиапазоны, которые разрешают только серверы. (Мне кажется странным), а затем рефлексивные отношения H < -> S дает вам проблемы, приводя к репликации общего типа. Хотя я думаю, что это странно смешивать server-> Subhandler и Handler-> субсервера, вы можете выполнить неограниченный 2 способ отношения, как вы хотите, извлекая интерфейсы, как это: (попробуйте, это работает)

interface IHandler 
{ 

} 
interface IServer 
{ 

} 
interface ISubServer extends IServer 
{ 
    public void subServerMethod(); 
} 
interface ISubHandler extends IHandler 
{ 
    public void subHandlerMethod(); 
} 

class Handler<S extends IServer> implements IHandler { 
    public S server; 

    public Handler(S server) { 
     this.server = server; 
    } 
} 

class SubHandler<S extends IServer> extends Handler<S> implements ISubHandler { 

    public SubHandler(S server) { 
     super(server); 
    } 

    public void subHandlerMethod() { 

    } 

} 

class Server<H extends IHandler> implements IServer{ 
    public ArrayList<H> handlers; 

    public Server() { 
     handlers = new ArrayList<H>(); 
    } 

    public void addHandler(H c) { 
     handlers.add(c); 
    } 
} 

class SubServer<H extends IHandler> extends Server<H> implements ISubServer { 
    public void subServerMethod() { 

    } 
} 



public class Example { 
public static void main(String[] args) { 
      Server<IHandler> serverWHandler = new Server<IHandler>(); 
      Server<ISubHandler> serverWSubHandler = new Server<ISubHandler>(); 

      SubServer<IHandler> subServerWHandler = new SubServer<IHandler>(); 
      SubServer<ISubHandler> subServerWSubHandler = new SubServer<ISubHandler>(); 

      Handler<IServer> handlerWServer = new Handler<IServer>(serverWHandler); 
      Handler<ISubServer> handlerWSubServer = new Handler<ISubServer>(subServerWHandler); 

      SubHandler<Server<ISubHandler>> subHandlerWServer = new SubHandler<Server<ISubHandler>>(serverWSubHandler); 
      SubHandler<SubServer<ISubHandler>> subHandlerWSubServer = new SubHandler<SubServer<ISubHandler>>(subServerWSubHandler); 

      serverWHandler.addHandler(handlerWServer); 
      subServerWHandler.addHandler(handlerWSubServer); 

      serverWSubHandler.addHandler(subHandlerWServer); 
      subServerWSubHandler.addHandler(subHandlerWSubServer); 

      subServerWHandler.subServerMethod(); 
      subServerWSubHandler.subServerMethod(); 

      handlerWSubServer.server.subServerMethod(); 
      subHandlerWSubServer.server.subServerMethod(); 

      subHandlerWServer.subHandlerMethod(); 
      subHandlerWSubServer.subHandlerMethod(); 

      System.out.println(subHandlerWSubServer.server.handlers.get(0).getClass().getName()); // SubHandler 

      //no longer produces an error: 
      subHandlerWSubServer.server.handlers.get(0).subHandlerMethod(); 

     } 

} 

и помните, что в любое время ваша проблема является рефлексивным отношением A < -> B, а не только с дженериками, вы можете извлечь IA и IB, чтобы A-> IB и B-> IA

+0

Спасибо! Но как я могу это исправить? – Feirell

+0

Если вы уверены, что в списке будет только SubHandlers, объявите его как список субандлеров. –

+0

Я только что редактировал вопрос, когда первая ошибка исчезла (сделана 'ArrayList >' to 'ArrayList '), но вторая по-прежнему там – Feirell

1

Он может работать с такой редизайном. Вы не сможете использовать сервер в качестве отправной точки, поскольку для этого потребуются общие аргументы, приводящие к рекурсивному определению. Вместо этого начните с ServerInfo. Как вы можете проверить это работает, но я думаю, что это может быть немного громоздким и не интуитивным.

class Server 
{ 

} 
class Handler 
{ 

} 
class SubServer extends Server 
{ 

} 
class SubHandler extends Handler 
{ 
    public void subHandlerMethod(){} 
} 

class HandlerInfo<S extends Server, H extends Handler> 
{ 
    ServerInfo<S,H> serverInfo; 
    H handler; 
} 
class ServerInfo<S extends Server, H extends Handler> 
{ 
    S server; 
    ArrayList<HandlerInfo<S,H>> handlerInfo = new ArrayList<>(); 
} 




public class Example { 
    public static void main(String[] args) { 
     Server s = new Server(); 
     SubHandler h = new SubHandler(); 
     ServerInfo<Server,SubHandler> serverInfo = new ServerInfo<>(); 
     HandlerInfo<Server,SubHandler> handlerInfo = new HandlerInfo<>(); 
     handlerInfo.serverInfo = serverInfo; 
     handlerInfo.handler = h; 
     serverInfo.server = s; 
     serverInfo.handlerInfo.add(handlerInfo); 
     serverInfo.handlerInfo.get(0).serverInfo.handlerInfo.get(0).handler.subHandlerMethod(); 

    } 
} 
+1

Благодарим за помощь! Этот подход, похоже, работает, что является фантастическим. Я пощажу, чтобы отметить это как правильное решение, чтобы посмотреть, не отвечает ли чистый дженерик. Не меньше спасибо за вашу идею. – Feirell

+0

снова - вы продолжаете настаивать на дженериковом ответе на вопрос, который вы не задали –